diff --git a/src/Rules/FunctionCallParametersCheck.php b/src/Rules/FunctionCallParametersCheck.php index 5c3821aa4f..955dad6383 100644 --- a/src/Rules/FunctionCallParametersCheck.php +++ b/src/Rules/FunctionCallParametersCheck.php @@ -578,6 +578,7 @@ private function processArguments( $newArguments = []; $namedArgumentAlreadyOccurred = false; + $namedArgumentsForVariadicParameter = []; foreach ($arguments as $i => [$argumentValue, $argumentValueType, $unpack, $argumentName, $argumentLine]) { if ($argumentName === null) { if (!isset($parameters[$i])) { @@ -603,12 +604,7 @@ private function processArguments( } else { $namedArgumentAlreadyOccurred = true; - $parametersCount = count($parameters); - if ( - !$isNativelyVariadic - || $parametersCount <= 0 - || $isBuiltin - ) { + if (!$isNativelyVariadic || $isBuiltin) { $errors[] = RuleErrorBuilder::message(sprintf($unknownParameterMessage, $argumentName)) ->identifier('argument.unknown') ->line($argumentLine) @@ -617,8 +613,17 @@ private function processArguments( continue; } + $parametersCount = count($parameters); $parameter = $parameters[$parametersCount - 1]; $originalParameter = $originalParameters[$parametersCount - 1]; + + if (isset($namedArgumentsForVariadicParameter[$argumentName])) { + $errors[] = RuleErrorBuilder::message(sprintf('Named parameter $%s overwrites previous argument.', $argumentName)) + ->identifier('argument.duplicate') + ->line($argumentLine) + ->build(); + } + $namedArgumentsForVariadicParameter[$argumentName] = true; } if ($namedArgumentAlreadyOccurred && $argumentName === null && !$unpack) { diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index b179c1ff0d..87f6d20496 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -512,6 +512,10 @@ public function testNamedArguments(): void 'Unknown parameter $a in call to function array_merge.', 14, ], + [ + 'Named parameter $var overwrites previous argument.', + 19, + ], ]; require_once __DIR__ . '/data/named-arguments-define.php'; diff --git a/tests/PHPStan/Rules/Functions/data/named-arguments.php b/tests/PHPStan/Rules/Functions/data/named-arguments.php index 3e349530b4..4cda8071d1 100644 --- a/tests/PHPStan/Rules/Functions/data/named-arguments.php +++ b/tests/PHPStan/Rules/Functions/data/named-arguments.php @@ -13,3 +13,8 @@ function baz(): void variadicFunction(...['a' => ['b', 'c']]); // works - userland array_merge(...['a' => ['b', 'c']]); // doesn't work - internal } + +function bug13710(): void +{ + variadicFunction(var: 1, var: 2); +}