diff --git a/packages/NodeTypeResolver/MethodParameterTypeResolver.php b/packages/NodeTypeResolver/MethodParameterTypeResolver.php index 057a89adca4..10bef9b43a6 100644 --- a/packages/NodeTypeResolver/MethodParameterTypeResolver.php +++ b/packages/NodeTypeResolver/MethodParameterTypeResolver.php @@ -6,11 +6,14 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\ClassMethod; +use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\Native\NativeMethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Type; use Rector\Core\Reflection\ReflectionResolver; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; final class MethodParameterTypeResolver { @@ -29,7 +32,7 @@ public function provideParameterTypesByStaticCall(StaticCall $staticCall): array return []; } - return $this->provideParameterTypesFromMethodReflection($methodReflection); + return $this->provideParameterTypesFromMethodReflection($methodReflection, $staticCall); } /** @@ -42,14 +45,16 @@ public function provideParameterTypesByClassMethod(ClassMethod $classMethod): ar return []; } - return $this->provideParameterTypesFromMethodReflection($methodReflection); + return $this->provideParameterTypesFromMethodReflection($methodReflection, $classMethod); } /** * @return Type[] */ - private function provideParameterTypesFromMethodReflection(MethodReflection $methodReflection): array - { + private function provideParameterTypesFromMethodReflection( + MethodReflection $methodReflection, + ClassMethod|StaticCall $node + ): array { if ($methodReflection instanceof NativeMethodReflection) { // method "getParameters()" does not exist there return []; @@ -57,7 +62,18 @@ private function provideParameterTypesFromMethodReflection(MethodReflection $met $parameterTypes = []; - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + if ($node instanceof ClassMethod) { + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + } else { + $scope = $node->getAttribute(AttributeKey::SCOPE); + + if (! $scope instanceof Scope) { + return []; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($methodReflection, $node, $scope); + } + foreach ($parametersAcceptor->getParameters() as $parameterReflection) { $parameterTypes[] = $parameterReflection->getType(); } diff --git a/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php index 0799a816561..4ee11577cb7 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php +++ b/packages/NodeTypeResolver/NodeTypeResolver/StaticCallMethodCallTypeResolver.php @@ -8,7 +8,6 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\Php\PhpMethodReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\MixedType; @@ -17,6 +16,7 @@ use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; use Symfony\Contracts\Service\Attribute\Required; /** @@ -75,7 +75,7 @@ public function resolve(Node $node): Type } foreach ($callerType->getReferencedClasses() as $referencedClass) { - $classMethodReturnType = $this->resolveClassMethodReturnType($referencedClass, $methodName, $scope); + $classMethodReturnType = $this->resolveClassMethodReturnType($referencedClass, $node, $methodName, $scope); if (! $classMethodReturnType instanceof MixedType) { return $classMethodReturnType; } @@ -84,8 +84,12 @@ public function resolve(Node $node): Type return new MixedType(); } - private function resolveClassMethodReturnType(string $referencedClass, string $methodName, Scope $scope): Type - { + private function resolveClassMethodReturnType( + string $referencedClass, + StaticCall|MethodCall $node, + string $methodName, + Scope $scope + ): Type { if (! $this->reflectionProvider->hasClass($referencedClass)) { return new MixedType(); } @@ -99,8 +103,10 @@ private function resolveClassMethodReturnType(string $referencedClass, string $m $methodReflection = $ancestorClassReflection->getMethod($methodName, $scope); if ($methodReflection instanceof PhpMethodReflection) { - $parametersAcceptorWithPhpDocs = ParametersAcceptorSelector::selectSingle( - $methodReflection->getVariants() + $parametersAcceptorWithPhpDocs = ParametersAcceptorSelectorVariantsWrapper::select( + $methodReflection, + $node, + $scope ); return $parametersAcceptorWithPhpDocs->getReturnType(); } diff --git a/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php b/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php index 446a1f8f6ff..a825c745c66 100644 --- a/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php @@ -8,11 +8,14 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; use PhpParser\Node\Stmt\ClassMethod; +use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use Rector\CodingStyle\Reflection\VendorLocationDetector; use Rector\Core\Rector\AbstractRector; use Rector\Core\Reflection\ReflectionResolver; +use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; use Rector\Php80\NodeResolver\ArgumentSorter; use Rector\Php80\NodeResolver\RequireOptionalParamResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -94,7 +97,7 @@ private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod return null; } - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($classMethodReflection); + $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($classMethodReflection, $classMethod); if ($expectedArgOrParamOrder === null) { return null; } @@ -118,7 +121,7 @@ private function refactorNew(New_ $new): ?New_ return null; } - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection); + $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $new); if ($expectedArgOrParamOrder === null) { return null; } @@ -135,7 +138,7 @@ private function refactorMethodCall(MethodCall $methodCall): ?MethodCall return null; } - $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection); + $expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $methodCall); if ($expectedArgOrParamOrder === null) { return null; } @@ -156,13 +159,26 @@ private function refactorMethodCall(MethodCall $methodCall): ?MethodCall /** * @return int[]|null */ - private function resolveExpectedArgParamOrderIfDifferent(MethodReflection $methodReflection): ?array - { + private function resolveExpectedArgParamOrderIfDifferent( + MethodReflection $methodReflection, + New_|MethodCall|ClassMethod $node + ): ?array { if ($this->vendorLocationDetector->detectMethodReflection($methodReflection)) { return null; } - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + if ($node instanceof ClassMethod) { + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + } else { + $scope = $node->getAttribute(AttributeKey::SCOPE); + + if (! $scope instanceof Scope) { + return null; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($methodReflection, $node, $scope); + } + $expectedParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection( $methodReflection );