Skip to content

Commit 56cb3b0

Browse files
Manage named arguments when replacing a value
1 parent a6f0ce5 commit 56cb3b0

File tree

5 files changed

+124
-7
lines changed

5 files changed

+124
-7
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
4+
5+
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg;
6+
7+
$object = new SomeMultiArg();
8+
$object->firstArgument(0, b: 1);
9+
$object->firstArgument(a: 0);
10+
$object->firstArgument(b: 1, a: 0);
11+
$object->secondArgument(0, b: 1);
12+
$object->secondArgument(b: 1);
13+
$object->secondArgument(b: 1, a: 0);
14+
15+
?>
16+
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
21+
22+
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg;
23+
24+
$object = new SomeMultiArg();
25+
$object->firstArgument(3, b: 1);
26+
$object->firstArgument(a: 3);
27+
$object->firstArgument(b: 1, a: 3);
28+
$object->secondArgument(0, b: 4);
29+
$object->secondArgument(b: 4);
30+
$object->secondArgument(b: 4, a: 0);
31+
32+
?>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Fixture;
4+
5+
use Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg;
6+
7+
use Symfony\Component\Yaml\Yaml;
8+
9+
$object = new SomeMultiArg();
10+
$object->firstArgument(b: 1);
11+
$object->secondArgument(a: 1);
12+
$object->secondArgument(0, c: 2);
13+
Yaml::parse('...', flags: false, other: false, another: true);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source;
6+
7+
class SomeMultiArg
8+
{
9+
public function firstArgument($a = 1, $b = 2)
10+
{
11+
}
12+
13+
public function secondArgument($a = 1, $b = 2, $c = 3)
14+
{
15+
}
16+
}

rules-tests/Arguments/Rector/ClassMethod/ReplaceArgumentDefaultValueRector/config/configured_rule.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,19 @@
6363
null,
6464
[]
6565
),
66+
new ReplaceArgumentDefaultValue(
67+
'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg',
68+
'firstArgument',
69+
0,
70+
0,
71+
3
72+
),
73+
new ReplaceArgumentDefaultValue(
74+
'Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\Source\SomeMultiArg',
75+
'secondArgument',
76+
1,
77+
1,
78+
4
79+
),
6680
]);
6781
};

rules/Arguments/ArgumentDefaultValueReplacer.php

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,22 @@
1212
use PhpParser\Node\Expr\MethodCall;
1313
use PhpParser\Node\Expr\New_;
1414
use PhpParser\Node\Expr\StaticCall;
15+
use PhpParser\Node\Expr\Variable;
1516
use PhpParser\Node\Stmt\ClassMethod;
1617
use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
1718
use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue;
19+
use Rector\NodeAnalyzer\ArgsAnalyzer;
20+
use Rector\PhpParser\AstResolver;
1821
use Rector\PhpParser\Node\NodeFactory;
1922
use Rector\PhpParser\Node\Value\ValueResolver;
2023

2124
final readonly class ArgumentDefaultValueReplacer
2225
{
2326
public function __construct(
2427
private NodeFactory $nodeFactory,
25-
private ValueResolver $valueResolver
28+
private ValueResolver $valueResolver,
29+
private ArgsAnalyzer $argsAnalyzer,
30+
private AstResolver $astResolver,
2631
) {
2732
}
2833

@@ -44,10 +49,6 @@ public function processReplaces(
4449
return $this->processParams($node, $replaceArgumentDefaultValue);
4550
}
4651

47-
if (! isset($node->args[$replaceArgumentDefaultValue->getPosition()])) {
48-
return null;
49-
}
50-
5152
return $this->processArgs($node, $replaceArgumentDefaultValue);
5253
}
5354

@@ -102,7 +103,48 @@ private function processArgs(
102103
}
103104

104105
$position = $replaceArgumentDefaultValue->getPosition();
105-
$particularArg = $expr->getArgs()[$position] ?? null;
106+
$arguments = $expr->getArgs();
107+
$firstNamedArgPosition = $this->argsAnalyzer->resolveFirstNamedArgPosition($arguments);
108+
// if the call has named argyments and we want to replace an array of values
109+
// we cannot replace it as we cannot really match this array of values to
110+
// the existing arguments, it would be too complex
111+
if ($firstNamedArgPosition !== null && is_array($replaceArgumentDefaultValue->getValueBefore())) {
112+
return null;
113+
}
114+
115+
// if the call has named arguments and the argument that we want to replace is not
116+
// before any named argument, we need to check if it is in the list of named arguments
117+
// if it is, we use the position of the named argyment as the position to replace
118+
// if it is not, we cannot replace it
119+
if ($firstNamedArgPosition !== null && $position >= $firstNamedArgPosition) {
120+
$call = $this->astResolver->resolveClassMethodOrFunctionFromCall($expr);
121+
if ($call === null) {
122+
return null;
123+
}
124+
125+
$paramName = null;
126+
$variable = $call->params[$position]->var;
127+
if ($variable instanceof Variable) {
128+
$paramName = $variable->name;
129+
}
130+
131+
$newPosition = -1;
132+
if (is_string($paramName)) {
133+
$newPosition = $this->argsAnalyzer->resolveArgPosition($arguments, $paramName, $newPosition);
134+
}
135+
136+
if ($newPosition === -1) {
137+
return null;
138+
}
139+
140+
$position = $newPosition;
141+
}
142+
143+
if (! isset($arguments[$position])) {
144+
return null;
145+
}
146+
147+
$particularArg = $arguments[$position] ?? null;
106148
if (! $particularArg instanceof Arg) {
107149
return null;
108150
}
@@ -111,7 +153,7 @@ private function processArgs(
111153
if (is_scalar(
112154
$replaceArgumentDefaultValue->getValueBefore()
113155
) && $argValue === $replaceArgumentDefaultValue->getValueBefore()) {
114-
$expr->args[$position] = $this->normalizeValueToArgument($replaceArgumentDefaultValue->getValueAfter());
156+
$particularArg->value = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter());
115157
return $expr;
116158
}
117159

0 commit comments

Comments
 (0)