Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6a4eb02

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Fix reordering unspecified named arguments
1 parent d20743d commit 6a4eb02

File tree

5 files changed

+164
-43
lines changed

5 files changed

+164
-43
lines changed

‎src/Analyser/ArgumentsNormalizer.php‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,20 @@ private static function reorderArgs(ParametersAcceptor $parametersAcceptor, Call
120120
return $callArgs;
121121
}
122122

123+
$hasVariadic = false;
123124
$argumentPositions = [];
124125
foreach ($signatureParameters as $i => $parameter) {
126+
if ($hasVariadic) {
127+
// variadic parameter must be last
128+
return null;
129+
}
130+
131+
$hasVariadic = $parameter->isVariadic();
125132
$argumentPositions[$parameter->getName()] = $i;
126133
}
127134

128135
$reorderedArgs = [];
136+
$additionalNamedArgs = [];
129137
foreach ($callArgs as $i => $arg) {
130138
if ($arg->name === null) {
131139
// add regular args as is
@@ -142,9 +150,33 @@ private static function reorderArgs(ParametersAcceptor $parametersAcceptor, Call
142150
$attributes,
143151
null,
144152
);
153+
} else {
154+
if (!$hasVariadic) {
155+
return null;
156+
}
157+
158+
$attributes = $arg->getAttributes();
159+
$attributes[self::ORIGINAL_ARG_ATTRIBUTE] = $arg;
160+
$additionalNamedArgs[] = new Arg(
161+
$arg->value,
162+
$arg->byRef,
163+
$arg->unpack,
164+
$attributes,
165+
null,
166+
);
145167
}
146168
}
147169

170+
// replace variadic parameter with additional named args, except if it is already set
171+
$additionalNamedArgsOffset = count($argumentPositions) - 1;
172+
if (array_key_exists($additionalNamedArgsOffset, $reorderedArgs)) {
173+
$additionalNamedArgsOffset++;
174+
}
175+
176+
foreach ($additionalNamedArgs as $i => $additionalNamedArg) {
177+
$reorderedArgs[$additionalNamedArgsOffset + $i] = $additionalNamedArg;
178+
}
179+
148180
if (count($reorderedArgs) === 0) {
149181
return [];
150182
}

‎tests/PHPStan/Analyser/ArgumentsNormalizerTest.php‎

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ public function dataReorderValid(): iterable
2626
{
2727
yield [
2828
[
29-
['one', false, null],
30-
['two', false, null],
31-
['three', false, null],
29+
['one', false, false, null],
30+
['two', false, false, null],
31+
['three', false, false, null],
3232
],
3333
[
3434
[new IntegerType(), null],
@@ -44,9 +44,9 @@ public function dataReorderValid(): iterable
4444

4545
yield [
4646
[
47-
['one', false, null],
48-
['two', false, null],
49-
['three', false, null],
47+
['one', false, false, null],
48+
['two', false, false, null],
49+
['three', false, false, null],
5050
],
5151
[
5252
[new IntegerType(), 'one'],
@@ -62,9 +62,9 @@ public function dataReorderValid(): iterable
6262

6363
yield [
6464
[
65-
['one', false, null],
66-
['two', false, null],
67-
['three', false, null],
65+
['one', false, false, null],
66+
['two', false, false, null],
67+
['three', false, false, null],
6868
],
6969
[
7070
[new StringType(), 'two'],
@@ -81,9 +81,9 @@ public function dataReorderValid(): iterable
8181
// could be invalid
8282
yield [
8383
[
84-
['one', false, null],
85-
['two', false, null],
86-
['three', false, null],
84+
['one', false, false, null],
85+
['two', false, false, null],
86+
['three', false, false, null],
8787
],
8888
[
8989
[new StringType(), 'two'],
@@ -97,9 +97,9 @@ public function dataReorderValid(): iterable
9797

9898
yield [
9999
[
100-
['one', false, null],
101-
['two', false, null],
102-
['three', false, null],
100+
['one', false, false, null],
101+
['two', false, false, null],
102+
['three', false, false, null],
103103
],
104104
[
105105
[new IntegerType(), null],
@@ -113,19 +113,19 @@ public function dataReorderValid(): iterable
113113

114114
yield [
115115
[
116-
['one', true, new IntegerType()],
117-
['two', true, new StringType()],
118-
['three', true, new FloatType()],
116+
['one', true, false, new IntegerType()],
117+
['two', true, false, new StringType()],
118+
['three', true, false, new FloatType()],
119119
],
120120
[],
121121
[],
122122
];
123123

124124
yield [
125125
[
126-
['one', true, new IntegerType()],
127-
['two', true, new StringType()],
128-
['three', true, new FloatType()],
126+
['one', true, false, new IntegerType()],
127+
['two', true, false, new StringType()],
128+
['three', true, false, new FloatType()],
129129
],
130130
[
131131
[new StringType(), 'two'],
@@ -138,9 +138,9 @@ public function dataReorderValid(): iterable
138138

139139
yield [
140140
[
141-
['one', true, new IntegerType()],
142-
['two', true, new StringType()],
143-
['three', true, new FloatType()],
141+
['one', true, false, new IntegerType()],
142+
['two', true, false, new StringType()],
143+
['three', true, false, new FloatType()],
144144
],
145145
[
146146
[new StringType(), 'one'],
@@ -152,35 +152,61 @@ public function dataReorderValid(): iterable
152152

153153
yield [
154154
[
155-
['one', true, new IntegerType()],
156-
['two', true, new StringType()],
157-
['three', true, new FloatType()],
155+
['one', true, false, new IntegerType()],
156+
['two', true, false, new StringType()],
157+
['three', true, false, new FloatType()],
158+
['rest', true, true, new StringType()],
158159
],
159160
[
160161
[new StringType(), 'onee'],
161162
],
162-
[],
163+
[
164+
new IntegerType(),
165+
new StringType(),
166+
new FloatType(),
167+
new StringType(),
168+
],
163169
];
164170

165171
yield [
166172
[
167-
['one', true, new IntegerType()],
168-
['two', true, new StringType()],
169-
['three', true, new FloatType()],
173+
['one', true, false, new IntegerType()],
174+
['two', true, false, new StringType()],
175+
['three', true, false, new FloatType()],
176+
['rest', true, true, new StringType()],
170177
],
171178
[
172179
[new IntegerType(), null],
173180
[new StringType(), 'onee'],
174181
],
175182
[
176183
new IntegerType(),
184+
new StringType(),
185+
new FloatType(),
186+
new StringType(),
187+
],
188+
];
189+
190+
yield [
191+
[
192+
['one', true, false, new IntegerType()],
193+
['rest', true, true, new StringType()],
194+
],
195+
[
196+
[new StringType(), 'rest'],
197+
[new StringType(), 'another'],
198+
],
199+
[
200+
new IntegerType(),
201+
new StringType(),
202+
new StringType(),
177203
],
178204
];
179205
}
180206

181207
/**
182208
* @dataProvider dataReorderValid
183-
* @param array<int, array{string, bool, ?Type}> $parameterSettings
209+
* @param array<int, array{string, bool, bool, ?Type}> $parameterSettings
184210
* @param array<int, array{Type, ?string}> $argumentSettings
185211
* @param array<int, Type> $expectedArgumentTypes
186212
*/
@@ -191,13 +217,13 @@ public function testReorderValid(
191217
): void
192218
{
193219
$parameters = [];
194-
foreach ($parameterSettings as [$name, $optional, $defaultValue]) {
220+
foreach ($parameterSettings as [$name, $optional, $variadic, $defaultValue]) {
195221
$parameters[] = new DummyParameter(
196222
$name,
197223
new MixedType(),
198224
$optional,
199225
null,
200-
false,
226+
$variadic,
201227
$defaultValue,
202228
);
203229
}
@@ -236,9 +262,9 @@ public function dataReorderInvalid(): iterable
236262
{
237263
yield [
238264
[
239-
['one', false, null],
240-
['two', false, null],
241-
['three', false, null],
265+
['one', false, false, null],
266+
['two', false, false, null],
267+
['three', false, false, null],
242268
],
243269
[
244270
[new StringType(), 'two'],
@@ -247,20 +273,43 @@ public function dataReorderInvalid(): iterable
247273

248274
yield [
249275
[
250-
['one', false, null],
251-
['two', false, null],
252-
['three', false, null],
276+
['one', false, false, null],
277+
['two', false, false, null],
278+
['three', false, false, null],
253279
],
254280
[
255281
[new IntegerType(), null],
256282
[new StringType(), 'three'],
257283
],
258284
];
285+
286+
yield [
287+
[
288+
['one', true, false, new IntegerType()],
289+
['two', true, false, new StringType()],
290+
['three', true, false, new FloatType()],
291+
],
292+
[
293+
[new StringType(), 'onee'],
294+
],
295+
];
296+
297+
yield [
298+
[
299+
['one', true, false, new IntegerType()],
300+
['two', true, false, new StringType()],
301+
['three', true, false, new FloatType()],
302+
],
303+
[
304+
[new IntegerType(), null],
305+
[new StringType(), 'onee'],
306+
],
307+
];
259308
}
260309

261310
/**
262311
* @dataProvider dataReorderInvalid
263-
* @param array<int, array{string, bool, ?Type}> $parameterSettings
312+
* @param array<int, array{string, bool, bool, ?Type}> $parameterSettings
264313
* @param array<int, array{Type, ?string}> $argumentSettings
265314
*/
266315
public function testReorderInvalid(
@@ -269,13 +318,13 @@ public function testReorderInvalid(
269318
): void
270319
{
271320
$parameters = [];
272-
foreach ($parameterSettings as [$name, $optional, $defaultValue]) {
321+
foreach ($parameterSettings as [$name, $optional, $variadic, $defaultValue]) {
273322
$parameters[] = new DummyParameter(
274323
$name,
275324
new MixedType(),
276325
$optional,
277326
null,
278-
false,
327+
$variadic,
279328
$defaultValue,
280329
);
281330
}

‎tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,13 @@ public function testBug6758(): void
7474
$this->analyse([__DIR__ . '/data/bug-6758.php'], []);
7575
}
7676

77+
public function testBug8204(): void
78+
{
79+
if (PHP_VERSION_ID < 80000) {
80+
$this->markTestSkipped('Test requires PHP 8.0.');
81+
}
82+
83+
$this->analyse([__DIR__ . '/data/bug-8204.php'], []);
84+
}
85+
7786
}

‎tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,15 @@ public function testBug6107(): void
254254
$this->analyse([__DIR__ . '/data/bug-6107.php'], []);
255255
}
256256

257+
public function testBug8204(): void
258+
{
259+
if (PHP_VERSION_ID < 80000) {
260+
$this->markTestSkipped('Test requires PHP 8.0.');
261+
}
262+
263+
$this->alwaysWrittenTags = [];
264+
$this->alwaysReadTags = [];
265+
$this->analyse([__DIR__ . '/data/bug-8204.php'], []);
266+
}
267+
257268
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1); // lint >= 8.0
2+
3+
namespace Bug8204;
4+
5+
function f(string ...$parameters) : void {
6+
}
7+
8+
class HelloWorld
9+
{
10+
private const FOO = 'foo';
11+
private string $bar = 'bar';
12+
13+
public function foobar(): void
14+
{
15+
f(
16+
foo: self::FOO,
17+
bar: $this->bar,
18+
);
19+
}
20+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /