55use PhpParser \Node \Expr \MethodCall ;
66use PHPStan \Analyser \Scope ;
77use PHPStan \Reflection \MethodReflection ;
8+ use PHPStan \Reflection \ParametersAcceptorSelector ;
89use PHPStan \Type \Accessory \AccessoryArrayListType ;
910use PHPStan \Type \ArrayType ;
1011use PHPStan \Type \DynamicMethodReturnTypeExtension ;
1112use PHPStan \Type \Generic \GenericObjectType ;
1213use PHPStan \Type \IntegerRangeType ;
1314use PHPStan \Type \IntersectionType ;
15+ use PHPStan \Type \ObjectType ;
1416use PHPStan \Type \Type ;
17+ use PHPStan \Type \TypeCombinator ;
1518use ReflectionAttribute ;
1619use function count ;
1720
@@ -32,8 +35,7 @@ public function getClass(): string
3235
3336 public function isMethodSupported (MethodReflection $ methodReflection ): bool
3437 {
35- return $ methodReflection ->getDeclaringClass ()->getName () === $ this ->className
36- && $ methodReflection ->getName () === 'getAttributes ' ;
38+ return $ methodReflection ->getName () === 'getAttributes ' ;
3739 }
3840
3941 public function getTypeFromMethodCall (MethodReflection $ methodReflection , MethodCall $ methodCall , Scope $ scope ): ?Type
@@ -44,7 +46,34 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
4446 $ argType = $ scope ->getType ($ methodCall ->getArgs ()[0 ]->value );
4547 $ classType = $ argType ->getClassStringObjectType ();
4648
47- return new IntersectionType ([new ArrayType (IntegerRangeType::createAllGreaterThanOrEqualTo (0 ), new GenericObjectType (ReflectionAttribute::class, [$ classType ])), new AccessoryArrayListType ()]);
49+ $ variant = ParametersAcceptorSelector::selectFromArgs ($ scope , $ methodCall ->getArgs (), $ methodReflection ->getVariants ());
50+ $ valueType = $ this ->resolveReflectionAttributeType ($ variant ->getReturnType (), $ classType );
51+
52+ return new IntersectionType ([new ArrayType (IntegerRangeType::createAllGreaterThanOrEqualTo (0 ), $ valueType ), new AccessoryArrayListType ()]);
53+ }
54+
55+ private function resolveReflectionAttributeType (Type $ returnType , Type $ classType ): Type
56+ {
57+ $ nativeReflectionAttributeType = new ObjectType (ReflectionAttribute::class);
58+
59+ $ valueTypes = [];
60+ foreach ($ returnType ->getIterableValueType ()->getObjectClassNames () as $ className ) {
61+ if (!$ nativeReflectionAttributeType ->isSuperTypeOf (new ObjectType ($ className ))->yes ()) {
62+ continue ;
63+ }
64+
65+ $ valueTypes [] = new GenericObjectType ($ className , [$ classType ]);
66+ }
67+
68+ if (count ($ valueTypes ) === 0 ) {
69+ return new GenericObjectType (ReflectionAttribute::class, [$ classType ]);
70+ }
71+
72+ if (count ($ valueTypes ) === 1 ) {
73+ return $ valueTypes [0 ];
74+ }
75+
76+ return TypeCombinator::union (...$ valueTypes );
4877 }
4978
5079}
0 commit comments