@@ -145,6 +145,10 @@ public function refactor(Node $node): ?Node
145145 return null ;
146146 }
147147
148+ if (! $ this ->isNarrowingValidFromParent ($ node , $ actualReturnClass )) {
149+ return null ;
150+ }
151+
148152 $ node ->returnType = new FullyQualified ($ actualReturnClass );
149153
150154 $ this ->updateDocblock ($ node , $ actualReturnClass );
@@ -235,6 +239,56 @@ private function isNarrowingValid(string $declaredType, string $actualType): boo
235239 ->yes ();
236240 }
237241
242+ private function isNarrowingValidFromParent (ClassMethod $ classMethod , string $ actualReturnClass ): bool
243+ {
244+ if ($ classMethod ->isPrivate ()) {
245+ return true ;
246+ }
247+
248+ $ classReflection = $ this ->reflectionResolver ->resolveClassReflection ($ classMethod );
249+
250+ if (! $ classReflection instanceof ClassReflection) {
251+ return true ;
252+ }
253+
254+ $ ancestors = array_filter (
255+ $ classReflection ->getAncestors (),
256+ fn (ClassReflection $ ancestorClassReflection ): bool => $ classReflection ->getName () !== $ ancestorClassReflection ->getName ()
257+ );
258+
259+ $ methodName = $ this ->getName ($ classMethod );
260+
261+ foreach ($ ancestors as $ ancestor ) {
262+ if ($ ancestor ->getFileName () === null ) {
263+ continue ;
264+ }
265+
266+ if (! $ ancestor ->hasNativeMethod ($ methodName )) {
267+ continue ;
268+ }
269+
270+ $ parentClassMethod = $ this ->astResolver ->resolveClassMethod ($ ancestor ->getName (), $ methodName );
271+
272+ if (! $ parentClassMethod instanceof ClassMethod) {
273+ continue ;
274+ }
275+
276+ $ parentReturnType = $ parentClassMethod ->returnType ;
277+
278+ if (! $ parentReturnType instanceof Node) {
279+ continue ;
280+ }
281+
282+ $ parentReturnTypeName = $ parentReturnType ->toString ();
283+
284+ if (! $ this ->isNarrowingValid ($ parentReturnTypeName , $ actualReturnClass )) {
285+ return false ;
286+ }
287+ }
288+
289+ return true ;
290+ }
291+
238292 private function getActualReturnClass (ClassMethod $ classMethod ): ?string
239293 {
240294 $ returnStatements = $ this ->betterNodeFinder ->findReturnsScoped ($ classMethod );
0 commit comments