From 95568aee410fd90caa5f576a0928dd8a05c8f92d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 20:49:30 +0000 Subject: [PATCH 1/2] fix ControllerMethodInjectionToConstructorRector removing call-site args when method params are moved to constructor When a helper method (e.g. problematic(LoggerInterface $logger, ?string $optional)) had a service param moved to the constructor, any $this->problematic($logger) call sites in other methods were not updated to drop the now-removed argument, producing invalid code. The fix tracks which parameter positions are removed per method and, after all existing transforms run, traverses every method body to remove the corresponding args from $this->methodName(...) call sites. Fixes https://github.com/rectorphp/rector/issues/9695 --- .../internal_method_call_arg_removal.php.inc | 56 +++++++++++++++++++ ...llerMethodInjectionToConstructorRector.php | 53 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/internal_method_call_arg_removal.php.inc diff --git a/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/internal_method_call_arg_removal.php.inc b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/internal_method_call_arg_removal.php.inc new file mode 100644 index 00000000..3f4c7e06 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/internal_method_call_arg_removal.php.inc @@ -0,0 +1,56 @@ +problematic($logger); + } + + public function problematic(LoggerInterface $logger, ?string $optional = 'test') + { + $logger->log('level', 'value'); + dump($optional); + } +} + +?> +----- +problematic(); + } + + public function problematic(?string $optional = 'test') + { + $this->logger->log('level', 'value'); + dump($optional); + } +} + +?> diff --git a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php index 23f6a317..a2627a7b 100644 --- a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php +++ b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php @@ -7,6 +7,7 @@ use Exception; use PhpParser\Node; use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name\FullyQualified; @@ -137,6 +138,9 @@ public function refactor(Node $node): ?Node /** @var array $methodParamNamesToReplace */ $methodParamNamesToReplace = []; + /** @var array $removedMethodArgPositions */ + $removedMethodArgPositions = []; + foreach ($node->getMethods() as $classMethod) { if ($this->shouldSkipClassMethod($classMethod)) { continue; @@ -223,6 +227,7 @@ public function refactor(Node $node): ?Node $paramsToRemove[] = [$classMethod, $key]; $propertyMetadatas[$paramName] = new PropertyMetadata($paramName, $paramType); $methodParamNamesToReplace[$classMethod->name->toString()][] = $paramName; + $removedMethodArgPositions[$classMethod->name->toString()][] = $key; } } @@ -254,9 +259,57 @@ public function refactor(Node $node): ?Node $this->replaceParamUseWithPropertyFetch($classMethod, $methodParamNamesToReplace[$methodName]); } + $this->updateCallSitesForRemovedParams($node, $removedMethodArgPositions); + return $node; } + /** + * @param array $removedArgPositionsByMethod + */ + private function updateCallSitesForRemovedParams(Class_ $node, array $removedArgPositionsByMethod): void + { + if ($removedArgPositionsByMethod === []) { + return; + } + + foreach ($node->getMethods() as $classMethod) { + if ($classMethod->stmts === null) { + continue; + } + + $this->traverseNodesWithCallable($classMethod->stmts, function (Node $node) use ( + $removedArgPositionsByMethod + ): ?MethodCall { + if (! $node instanceof MethodCall) { + return null; + } + + if (! $node->var instanceof Variable) { + return null; + } + + if (! $this->isName($node->var, 'this')) { + return null; + } + + $methodName = $this->getName($node->name); + if ($methodName === null || ! isset($removedArgPositionsByMethod[$methodName])) { + return null; + } + + $removedPositions = $removedArgPositionsByMethod[$methodName]; + rsort($removedPositions); + foreach ($removedPositions as $position) { + unset($node->args[$position]); + } + + $node->args = array_values($node->args); + return $node; + }); + } + } + private function shouldSkipClassMethod(ClassMethod $classMethod): bool { if ($classMethod->isMagic() && ! $this->isName($classMethod->name, MethodName::INVOKE)) { From 5d49bd89e1607ef3de86d26a525c6c5109b69dba Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 16 May 2026 20:50:35 +0000 Subject: [PATCH 2/2] [rector] Rector fixes --- .../ControllerMethodInjectionToConstructorRector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php index a2627a7b..376d2ad3 100644 --- a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php +++ b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php @@ -267,13 +267,13 @@ public function refactor(Node $node): ?Node /** * @param array $removedArgPositionsByMethod */ - private function updateCallSitesForRemovedParams(Class_ $node, array $removedArgPositionsByMethod): void + private function updateCallSitesForRemovedParams(Class_ $class, array $removedArgPositionsByMethod): void { if ($removedArgPositionsByMethod === []) { return; } - foreach ($node->getMethods() as $classMethod) { + foreach ($class->getMethods() as $classMethod) { if ($classMethod->stmts === null) { continue; } @@ -300,8 +300,8 @@ private function updateCallSitesForRemovedParams(Class_ $node, array $removedArg $removedPositions = $removedArgPositionsByMethod[$methodName]; rsort($removedPositions); - foreach ($removedPositions as $position) { - unset($node->args[$position]); + foreach ($removedPositions as $removedPosition) { + unset($node->args[$removedPosition]); } $node->args = array_values($node->args);