Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/Analyser/ArgumentsNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,20 @@ private static function reorderArgs(ParametersAcceptor $parametersAcceptor, Call
return $callArgs;
}

$hasVariadic = false;
$argumentPositions = [];
foreach ($signatureParameters as $i => $parameter) {
if ($hasVariadic) {
// variadic parameter must be last
return null;
}

$hasVariadic = $parameter->isVariadic();
$argumentPositions[$parameter->getName()] = $i;
}

$reorderedArgs = [];
$additionalNamedArgs = [];
foreach ($callArgs as $i => $arg) {
if ($arg->name === null) {
// add regular args as is
Expand All @@ -142,9 +150,33 @@ private static function reorderArgs(ParametersAcceptor $parametersAcceptor, Call
$attributes,
null,
);
} else {
if (!$hasVariadic) {
return null;
}

$attributes = $arg->getAttributes();
$attributes[self::ORIGINAL_ARG_ATTRIBUTE] = $arg;
$additionalNamedArgs[] = new Arg(
$arg->value,
$arg->byRef,
$arg->unpack,
$attributes,
null,
);
}
}

// replace variadic parameter with additional named args, except if it is already set
$additionalNamedArgsOffset = count($argumentPositions) - 1;
if (array_key_exists($additionalNamedArgsOffset, $reorderedArgs)) {
$additionalNamedArgsOffset++;
}

foreach ($additionalNamedArgs as $i => $additionalNamedArg) {
$reorderedArgs[$additionalNamedArgsOffset + $i] = $additionalNamedArg;
}

if (count($reorderedArgs) === 0) {
return [];
}
Expand Down
135 changes: 92 additions & 43 deletions tests/PHPStan/Analyser/ArgumentsNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public function dataReorderValid(): iterable
{
yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new IntegerType(), null],
Expand All @@ -44,9 +44,9 @@ public function dataReorderValid(): iterable

yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new IntegerType(), 'one'],
Expand All @@ -62,9 +62,9 @@ public function dataReorderValid(): iterable

yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new StringType(), 'two'],
Expand All @@ -81,9 +81,9 @@ public function dataReorderValid(): iterable
// could be invalid
yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new StringType(), 'two'],
Expand All @@ -97,9 +97,9 @@ public function dataReorderValid(): iterable

yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new IntegerType(), null],
Expand All @@ -113,19 +113,19 @@ public function dataReorderValid(): iterable

yield [
[
['one', true, new IntegerType()],
['two', true, new StringType()],
['three', true, new FloatType()],
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
],
[],
[],
];

yield [
[
['one', true, new IntegerType()],
['two', true, new StringType()],
['three', true, new FloatType()],
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
],
[
[new StringType(), 'two'],
Expand All @@ -138,9 +138,9 @@ public function dataReorderValid(): iterable

yield [
[
['one', true, new IntegerType()],
['two', true, new StringType()],
['three', true, new FloatType()],
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
],
[
[new StringType(), 'one'],
Expand All @@ -152,35 +152,61 @@ public function dataReorderValid(): iterable

yield [
[
['one', true, new IntegerType()],
['two', true, new StringType()],
['three', true, new FloatType()],
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
['rest', true, true, new StringType()],
],
[
[new StringType(), 'onee'],
],
[],
[
new IntegerType(),
new StringType(),
new FloatType(),
new StringType(),
],
];

yield [
[
['one', true, new IntegerType()],
['two', true, new StringType()],
['three', true, new FloatType()],
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
['rest', true, true, new StringType()],
],
[
[new IntegerType(), null],
[new StringType(), 'onee'],
],
[
new IntegerType(),
new StringType(),
new FloatType(),
new StringType(),
],
];

yield [
[
['one', true, false, new IntegerType()],
['rest', true, true, new StringType()],
],
[
[new StringType(), 'rest'],
[new StringType(), 'another'],
],
[
new IntegerType(),
new StringType(),
new StringType(),
],
];
}

/**
* @dataProvider dataReorderValid
* @param array<int, array{string, bool, ?Type}> $parameterSettings
* @param array<int, array{string, bool, bool, ?Type}> $parameterSettings
* @param array<int, array{Type, ?string}> $argumentSettings
* @param array<int, Type> $expectedArgumentTypes
*/
Expand All @@ -191,13 +217,13 @@ public function testReorderValid(
): void
{
$parameters = [];
foreach ($parameterSettings as [$name, $optional, $defaultValue]) {
foreach ($parameterSettings as [$name, $optional, $variadic, $defaultValue]) {
$parameters[] = new DummyParameter(
$name,
new MixedType(),
$optional,
null,
false,
$variadic,
$defaultValue,
);
}
Expand Down Expand Up @@ -236,9 +262,9 @@ public function dataReorderInvalid(): iterable
{
yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new StringType(), 'two'],
Expand All @@ -247,20 +273,43 @@ public function dataReorderInvalid(): iterable

yield [
[
['one', false, null],
['two', false, null],
['three', false, null],
['one', false, false, null],
['two', false, false, null],
['three', false, false, null],
],
[
[new IntegerType(), null],
[new StringType(), 'three'],
],
];

yield [
[
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
],
[
[new StringType(), 'onee'],
],
];

yield [
[
['one', true, false, new IntegerType()],
['two', true, false, new StringType()],
['three', true, false, new FloatType()],
],
[
[new IntegerType(), null],
[new StringType(), 'onee'],
],
];
}

/**
* @dataProvider dataReorderInvalid
* @param array<int, array{string, bool, ?Type}> $parameterSettings
* @param array<int, array{string, bool, bool, ?Type}> $parameterSettings
* @param array<int, array{Type, ?string}> $argumentSettings
*/
public function testReorderInvalid(
Expand All @@ -269,13 +318,13 @@ public function testReorderInvalid(
): void
{
$parameters = [];
foreach ($parameterSettings as [$name, $optional, $defaultValue]) {
foreach ($parameterSettings as [$name, $optional, $variadic, $defaultValue]) {
$parameters[] = new DummyParameter(
$name,
new MixedType(),
$optional,
null,
false,
$variadic,
$defaultValue,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,13 @@ public function testBug6758(): void
$this->analyse([__DIR__ . '/data/bug-6758.php'], []);
}

public function testBug8204(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->analyse([__DIR__ . '/data/bug-8204.php'], []);
}

}
11 changes: 11 additions & 0 deletions tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,15 @@ public function testBug6107(): void
$this->analyse([__DIR__ . '/data/bug-6107.php'], []);
}

public function testBug8204(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->alwaysWrittenTags = [];
$this->alwaysReadTags = [];
$this->analyse([__DIR__ . '/data/bug-8204.php'], []);
}

}
20 changes: 20 additions & 0 deletions tests/PHPStan/Rules/DeadCode/data/bug-8204.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types = 1); // lint >= 8.0

namespace Bug8204;

function f(string ...$parameters) : void {
}

class HelloWorld
{
private const FOO = 'foo';
private string $bar = 'bar';

public function foobar(): void
{
f(
foo: self::FOO,
bar: $this->bar,
);
}
}