1010use PHPStan \PhpDocParser \Ast \PhpDoc \ReturnTagValueNode ;
1111use PHPStan \PhpDocParser \Ast \Type \IdentifierTypeNode ;
1212use PHPStan \Reflection \ClassReflection ;
13+ use PHPStan \Reflection \ParametersAcceptorSelector ;
1314use PHPStan \Reflection \ReflectionProvider ;
1415use PHPStan \Type \Generic \GenericObjectType ;
1516use PHPStan \Type \ObjectType ;
1819use Rector \BetterPhpDocParser \ValueObject \Type \FullyQualifiedIdentifierTypeNode ;
1920use Rector \Comments \NodeDocBlock \DocBlockUpdater ;
2021use Rector \NodeTypeResolver \TypeComparator \TypeComparator ;
21- use Rector \PhpParser \AstResolver ;
2222use Rector \PhpParser \Node \BetterNodeFinder ;
2323use Rector \Rector \AbstractRector ;
2424use Rector \Reflection \ReflectionResolver ;
@@ -40,10 +40,6 @@ final class NarrowObjectReturnTypeRector extends AbstractRector
4040 * @readonly
4141 */
4242 private ReflectionResolver $ reflectionResolver ;
43- /**
44- * @readonly
45- */
46- private AstResolver $ astResolver ;
4743 /**
4844 * @readonly
4945 */
@@ -64,11 +60,10 @@ final class NarrowObjectReturnTypeRector extends AbstractRector
6460 * @readonly
6561 */
6662 private ReflectionProvider $ reflectionProvider ;
67- public function __construct (BetterNodeFinder $ betterNodeFinder , ReflectionResolver $ reflectionResolver , AstResolver $ astResolver , StaticTypeMapper $ staticTypeMapper , TypeComparator $ typeComparator , PhpDocInfoFactory $ phpDocInfoFactory , DocBlockUpdater $ docBlockUpdater , ReflectionProvider $ reflectionProvider )
63+ public function __construct (BetterNodeFinder $ betterNodeFinder , ReflectionResolver $ reflectionResolver , StaticTypeMapper $ staticTypeMapper , TypeComparator $ typeComparator , PhpDocInfoFactory $ phpDocInfoFactory , DocBlockUpdater $ docBlockUpdater , ReflectionProvider $ reflectionProvider )
6864 {
6965 $ this ->betterNodeFinder = $ betterNodeFinder ;
7066 $ this ->reflectionResolver = $ reflectionResolver ;
71- $ this ->astResolver = $ astResolver ;
7267 $ this ->staticTypeMapper = $ staticTypeMapper ;
7368 $ this ->typeComparator = $ typeComparator ;
7469 $ this ->phpDocInfoFactory = $ phpDocInfoFactory ;
@@ -224,23 +219,25 @@ private function isNarrowingValidFromParent(ClassMethod $classMethod, string $ac
224219 }
225220 $ ancestors = array_filter ($ classReflection ->getAncestors (), fn (ClassReflection $ ancestorClassReflection ): bool => $ classReflection ->getName () !== $ ancestorClassReflection ->getName ());
226221 $ methodName = $ this ->getName ($ classMethod );
222+ $ actualObjectType = new ObjectType ($ actualReturnClass );
227223 foreach ($ ancestors as $ ancestor ) {
228224 if ($ ancestor ->getFileName () === null ) {
229225 continue ;
230226 }
231227 if (!$ ancestor ->hasNativeMethod ($ methodName )) {
232228 continue ;
233229 }
234- $ parentClassMethod = $ this ->astResolver ->resolveClassMethod ($ ancestor ->getName (), $ methodName );
235- if (!$ parentClassMethod instanceof ClassMethod) {
230+ $ parametersAcceptor = ParametersAcceptorSelector::combineAcceptors ($ ancestor ->getNativeMethod ($ methodName )->getVariants ());
231+ // only a single, bare class-name return type can constrain narrowing
232+ $ parentNativeReturnType = $ parametersAcceptor ->getNativeReturnType ();
233+ if (!$ parentNativeReturnType instanceof ObjectType) {
236234 continue ;
237235 }
238- $ parentReturnType = $ parentClassMethod ->returnType ;
239- if (!$ parentReturnType instanceof Identifier && !$ parentReturnType instanceof FullyQualified) {
240- continue ;
236+ if (!$ parentNativeReturnType ->isSuperTypeOf ($ actualObjectType )->yes ()) {
237+ return \false;
241238 }
242- $ parentReturnTypeName = $ parentReturnType -> toString ();
243- if (! $ this -> isNarrowingValid ( $ parentClassMethod , $ parentReturnTypeName , $ actualReturnClass ) ) {
239+ // skip narrowing when the parent @return is a generic type, e.g. Collection<int, Foo>
240+ if ($ parametersAcceptor -> getReturnType () instanceof GenericObjectType ) {
244241 return \false;
245242 }
246243 }
0 commit comments