2727use PHPStan \Type \VerbosityLevel ;
2828use function array_fill ;
2929use function array_key_exists ;
30- use function array_slice ;
3130use function count ;
3231use function implode ;
3332use function in_array ;
@@ -82,22 +81,19 @@ public function check(
8281 {
8382 $ functionParametersMinCount = 0 ;
8483 $ functionParametersMaxCount = 0 ;
85- $ functionParameterNames = [];
8684 foreach ($ parametersAcceptor ->getParameters () as $ parameter ) {
8785 if (!$ parameter ->isOptional ()) {
8886 $ functionParametersMinCount ++;
8987 }
9088
9189 $ functionParametersMaxCount ++;
92-
93- $ functionParameterNames [] = $ parameter ->getName ();
9490 }
9591
9692 if ($ parametersAcceptor ->isVariadic ()) {
9793 $ functionParametersMaxCount = -1 ;
9894 }
9995
100- /** @var array<int, array{Expr, Type|null, bool, string|null, int}> $arguments */
96+ /** @var array<int, array{Expr, Type|null, bool, string|null, int, bool }> $arguments */
10197 $ arguments = [];
10298 /** @var array<int, Node\Arg> $args */
10399 $ args = $ funcCall ->getArgs ();
@@ -111,6 +107,8 @@ public function check(
111107 $ argumentName = $ arg ->name ->toString ();
112108 }
113109
110+ $ nonUnpackAfterUnpacked = false ;
111+
114112 if ($ hasNamedArguments && $ arg ->unpack ) {
115113 $ errors [] = RuleErrorBuilder::message ('Named argument cannot be followed by an unpacked (...) argument. ' )
116114 ->identifier ('argument.unpackAfterNamed ' )
@@ -119,15 +117,9 @@ public function check(
119117 ->build ();
120118 }
121119 if ($ hasUnpackedArgument && !$ arg ->unpack ) {
122- if ($ argumentName !== null && $ scope ->getPhpVersion ()->supportsNamedArgumentAfterUnpackedArgument ()->yes ()) {
123- if (in_array ($ argumentName , array_slice ($ functionParameterNames , 0 , count ($ arguments )), true )) {
124- $ errors [] = RuleErrorBuilder::message (sprintf ('Named parameter cannot overwrite already unpacked argument $%s. ' , $ argumentName ))
125- ->identifier ('argument.namedOverwriteAfterUnpacked ' )
126- ->line ($ arg ->getStartLine ())
127- ->nonIgnorable ()
128- ->build ();
129- }
130- } else {
120+ $ nonUnpackAfterUnpacked = true ;
121+
122+ if ($ argumentName === null || !$ scope ->getPhpVersion ()->supportsNamedArgumentAfterUnpackedArgument ()->yes ()) {
131123 $ errors [] = RuleErrorBuilder::message ('Unpacked argument (...) cannot be followed by a non-unpacked argument. ' )
132124 ->identifier ('argument.nonUnpackAfterUnpacked ' )
133125 ->line ($ arg ->getStartLine ())
@@ -191,6 +183,7 @@ public function check(
191183 false ,
192184 $ keyArgumentName ,
193185 $ arg ->getStartLine (),
186+ $ nonUnpackAfterUnpacked ,
194187 ];
195188 }
196189 } else {
@@ -200,6 +193,7 @@ public function check(
200193 true ,
201194 null ,
202195 $ arg ->getStartLine (),
196+ $ nonUnpackAfterUnpacked ,
203197 ];
204198 }
205199 continue ;
@@ -211,6 +205,7 @@ public function check(
211205 false ,
212206 $ argumentName ,
213207 $ arg ->getStartLine (),
208+ $ nonUnpackAfterUnpacked ,
214209 ];
215210 }
216211
@@ -562,7 +557,7 @@ private function processArguments(
562557 $ newArguments = [];
563558
564559 $ namedArgumentAlreadyOccurred = false ;
565- foreach ($ arguments as $ i => [$ argumentValue , $ argumentValueType , $ unpack , $ argumentName , $ argumentLine ]) {
560+ foreach ($ arguments as $ i => [$ argumentValue , $ argumentValueType , $ unpack , $ argumentName , $ argumentLine, $ nonUnpackAfterUnpacked ]) {
566561 if ($ argumentName === null ) {
567562 if (!isset ($ parameters [$ i ])) {
568563 if (!$ parametersAcceptor ->isVariadic () || count ($ parameters ) === 0 ) {
@@ -622,10 +617,19 @@ private function processArguments(
622617 && !$ parameter ->isVariadic ()
623618 && !array_key_exists ($ parameter ->getName (), $ unusedParametersByName )
624619 ) {
625- $ errors [] = RuleErrorBuilder::message (sprintf ('Argument for parameter $%s has already been passed. ' , $ parameter ->getName ()))
626- ->identifier ('argument.duplicate ' )
627- ->line ($ argumentLine )
628- ->build ();
620+ if ($ nonUnpackAfterUnpacked ) {
621+ $ errors [] = RuleErrorBuilder::message (sprintf ('Named parameter cannot overwrite already unpacked argument $%s. ' , $ argumentName ))
622+ ->identifier ('argument.namedOverwriteAfterUnpacked ' )
623+ ->line ($ argumentLine )
624+ ->nonIgnorable ()
625+ ->build ();
626+ } else {
627+ $ errors [] = RuleErrorBuilder::message (sprintf ('Argument for parameter $%s has already been passed. ' , $ parameter ->getName ()))
628+ ->identifier ('argument.duplicate ' )
629+ ->line ($ argumentLine )
630+ ->build ();
631+ }
632+
629633 continue ;
630634 }
631635
0 commit comments