From 63d0e2978827abdd98ba5e97e31bd72d0d5743db Mon Sep 17 00:00:00 2001 From: Carlos Granados Date: Thu, 15 Jan 2026 14:27:50 +0100 Subject: [PATCH] Manage named arguments when removing values --- .../Fixture/remove_named_arguments.php.inc | 32 ++++++++++++++ .../Fixture/skip_named_arguments.php.inc | 10 +++++ .../Source/SomeMultiArg.php | 16 +++++++ .../config/configured_rule.php | 3 ++ .../RemoveMethodCallParamRector.php | 44 ++++++++++++++++++- 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc create mode 100644 rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc create mode 100644 rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/SomeMultiArg.php diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc new file mode 100644 index 00000000000..682f5bcc919 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/remove_named_arguments.php.inc @@ -0,0 +1,32 @@ +firstArgument(0, b: 1); +$object->firstArgument(a: 0); +$object->firstArgument(b: 1, a: 0); +$object->secondArgument(0, b: 1); +$object->secondArgument(b: 1); +$object->secondArgument(b: 1, a: 0); + +?> + +----- +firstArgument(b: 1); +$object->firstArgument(); +$object->firstArgument(b: 1); +$object->secondArgument(0); +$object->secondArgument(); +$object->secondArgument(a: 0); + +?> diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc new file mode 100644 index 00000000000..bf33f0f5840 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Fixture/skip_named_arguments.php.inc @@ -0,0 +1,10 @@ +firstArgument(b: 1); +$object->secondArgument(a: 1); +$object->secondArgument(0, c: 2); diff --git a/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/SomeMultiArg.php b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/SomeMultiArg.php new file mode 100644 index 00000000000..5aa0cb7b551 --- /dev/null +++ b/rules-tests/Arguments/Rector/MethodCall/RemoveMethodCallParamRector/Source/SomeMultiArg.php @@ -0,0 +1,16 @@ +ruleWithConfiguration(RemoveMethodCallParamRector::class, [ new RemoveMethodCallParam(MethodCaller::class, 'process', 1), new RemoveMethodCallParam(StaticCaller::class, 'remove', 3), + new RemoveMethodCallParam(SomeMultiArg::class, 'firstArgument', 0), + new RemoveMethodCallParam(SomeMultiArg::class, 'secondArgument', 1), ]); }; diff --git a/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php b/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php index 7427216eb84..e70daba48d2 100644 --- a/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php +++ b/rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php @@ -7,8 +7,11 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Expr\Variable; use Rector\Arguments\ValueObject\RemoveMethodCallParam; use Rector\Contract\Rector\ConfigurableRectorInterface; +use Rector\NodeAnalyzer\ArgsAnalyzer; +use Rector\PhpParser\AstResolver; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -25,6 +28,12 @@ final class RemoveMethodCallParamRector extends AbstractRector implements Config */ private array $removeMethodCallParams = []; + public function __construct( + private readonly AstResolver $astResolver, + private readonly ArgsAnalyzer $argsAnalyzer, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Remove parameter of method call', [ @@ -83,11 +92,42 @@ public function refactor(Node $node): ?Node } $args = $node->getArgs(); - if (! isset($args[$removeMethodCallParam->getParamPosition()])) { + $position = $removeMethodCallParam->getParamPosition(); + $firstNamedArgPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($args); + + // if the call has named arguments and the argument that we want to remove is not + // before any named argument, we need to check if it is in the list of named arguments + // if it is, we use the position of the named argument as the position to remove + // if it is not, we cannot remove it + if ($firstNamedArgPosition !== null && $position >= $firstNamedArgPosition) { + $call = $this->astResolver->resolveClassMethodOrFunctionFromCall($node); + if ($call === null) { + return null; + } + + $paramName = null; + $variable = $call->params[$position]->var; + if ($variable instanceof Variable) { + $paramName = $variable->name; + } + + $newPosition = -1; + if (is_string($paramName)) { + $newPosition = $this->argsAnalyzer->resolveArgPosition($args, $paramName, $newPosition); + } + + if ($newPosition === -1) { + return null; + } + + $position = $newPosition; + } + + if (! isset($args[$position])) { continue; } - unset($node->args[$removeMethodCallParam->getParamPosition()]); + unset($node->args[$position]); $hasChanged = true; }