|
19 | 19 | use PHPStan\Reflection\ParametersAcceptorSelector; |
20 | 20 | use PHPStan\Type\ClosureType; |
21 | 21 | use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; |
| 22 | +use PHPStan\Type\IntersectionType; |
22 | 23 | use PHPStan\Type\NeverType; |
23 | 24 | use PHPStan\Type\Type; |
24 | 25 | use PHPStan\Type\TypeCombinator; |
| 26 | +use PHPStan\Type\TypeTraverser; |
| 27 | +use PHPStan\Type\UnionType; |
25 | 28 |
|
26 | 29 | final class ReflectionHelperGetPrivateMethodInvokerReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension |
27 | 30 | { |
@@ -53,44 +56,57 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, |
53 | 56 | $objectType = $scope->getType($args[0]->value)->getObjectTypeOrClassStringObjectType(); |
54 | 57 | $methodType = $scope->getType($args[1]->value); |
55 | 58 |
|
56 | | - if ($objectType->getObjectClassReflections() === [] && ! $objectType->isObject()->yes()) { |
| 59 | + if (! $objectType->isObject()->yes()) { |
57 | 60 | return new NeverType(true); |
58 | 61 | } |
59 | 62 |
|
60 | | - $closures = []; |
| 63 | + return TypeTraverser::map($objectType, static function (Type $type, callable $traverse) use ($methodType, $scope, $args, $methodReflection): Type { |
| 64 | + if ($type instanceof UnionType || $type instanceof IntersectionType) { |
| 65 | + return $traverse($type); |
| 66 | + } |
| 67 | + |
| 68 | + $closures = []; |
| 69 | + |
| 70 | + foreach ($type->getObjectClassReflections() as $classReflection) { |
| 71 | + foreach ($methodType->getConstantStrings() as $methodStringType) { |
| 72 | + $methodName = $methodStringType->getValue(); |
| 73 | + |
| 74 | + if (! $classReflection->hasMethod($methodName)) { |
| 75 | + $closures[] = new NeverType(true); |
| 76 | + |
| 77 | + continue; |
| 78 | + } |
| 79 | + |
| 80 | + $invokedMethodReflection = $classReflection->getMethod($methodName, $scope); |
61 | 81 |
|
62 | | - foreach ($objectType->getObjectClassReflections() as $classReflection) { |
63 | | - foreach ($methodType->getConstantStrings() as $methodStringType) { |
64 | | - $methodName = $methodStringType->getValue(); |
| 82 | + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( |
| 83 | + $scope, |
| 84 | + [], |
| 85 | + $invokedMethodReflection->getVariants(), |
| 86 | + $invokedMethodReflection->getNamedArgumentsVariants(), |
| 87 | + ); |
65 | 88 |
|
66 | | - if (! $classReflection->hasMethod($methodName)) { |
67 | | - $closures[] = new NeverType(true); |
| 89 | + $returnType = strtolower($methodName) === '__construct' ? $type : $parametersAcceptor->getReturnType(); |
68 | 90 |
|
69 | | - continue; |
| 91 | + $closures[] = new ClosureType( |
| 92 | + $parametersAcceptor->getParameters(), |
| 93 | + $returnType, |
| 94 | + $parametersAcceptor->isVariadic(), |
| 95 | + $parametersAcceptor->getTemplateTypeMap(), |
| 96 | + $parametersAcceptor->getResolvedTemplateTypeMap(), |
| 97 | + ); |
70 | 98 | } |
| 99 | + } |
71 | 100 |
|
72 | | - $methodReflection = $classReflection->getMethod($methodName, $scope); |
73 | | - $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( |
| 101 | + if ($closures === []) { |
| 102 | + return ParametersAcceptorSelector::selectFromArgs( |
74 | 103 | $scope, |
75 | 104 | $args, |
76 | 105 | $methodReflection->getVariants(), |
77 | | - $methodReflection->getNamedArgumentsVariants(), |
78 | | - ); |
79 | | - |
80 | | - $closures[] = new ClosureType( |
81 | | - $parametersAcceptor->getParameters(), |
82 | | - $parametersAcceptor->getReturnType(), |
83 | | - $parametersAcceptor->isVariadic(), |
84 | | - $parametersAcceptor->getTemplateTypeMap(), |
85 | | - $parametersAcceptor->getResolvedTemplateTypeMap(), |
86 | | - ); |
| 106 | + )->getReturnType(); |
87 | 107 | } |
88 | | - } |
89 | | - |
90 | | - if ($closures === []) { |
91 | | - return null; |
92 | | - } |
93 | 108 |
|
94 | | - return TypeCombinator::union(...$closures); |
| 109 | + return TypeCombinator::union(...$closures); |
| 110 | + }); |
95 | 111 | } |
96 | 112 | } |
0 commit comments