66
77use InvalidArgumentException ;
88use PhpParser \Node ;
9+ use PhpParser \ConstExprEvaluator ;
10+ use PhpParser \Node \Arg ;
911use PhpParser \Node \Expr \FuncCall ;
1012use PhpParser \Node \Expr \MethodCall ;
1113use PhpParser \Node \Expr \New_ ;
@@ -37,11 +39,14 @@ final class AddNamedArgumentsRector extends AbstractRector implements MinPhpVers
3739
3840 private readonly Reflection $ reflectionService ;
3941
42+ private readonly ConstExprEvaluator $ constExprEvaluator ;
43+
4044 public function __construct (
4145 ReflectionProvider $ reflectionProvider ,
4246 NodeNameResolver $ nodeNameResolver ,
4347 NodeTypeResolver $ nodeTypeResolver ,
4448 ?Reflection $ reflectionService = null ,
49+ ?ConstExprEvaluator $ constExprEvaluator = null ,
4550 ) {
4651 if ($ reflectionService === null ) {
4752 $ reflectionService = new Reflection (
@@ -51,6 +56,13 @@ public function __construct(
5156 );
5257 }
5358 $ this ->reflectionService = $ reflectionService ;
59+ $ this ->constExprEvaluator = $ constExprEvaluator ?? new ConstExprEvaluator (static function (string $ name ) {
60+ if (\defined ($ name )) {
61+ return \constant ($ name );
62+ }
63+
64+ throw new \RuntimeException ("Undefined constant: {$ name }" );
65+ });
5466 }
5567
5668 public function getRuleDefinition (): RuleDefinition
@@ -90,14 +102,43 @@ private function addNamesToArgs(
90102 FuncCall |StaticCall |MethodCall |New_ $ node ,
91103 array $ parameters ,
92104 ): void {
93- $ argNames = [];
105+ $ namedArgs = [];
94106 foreach ($ node ->args as $ index => $ arg ) {
95- $ argNames [$ index ] = new Identifier (name: $ parameters [$ index ]->getName ());
107+ $ parameter = $ parameters [$ index ] ?? null ;
108+ if ($ parameter === null ) {
109+ $ namedArgs [] = $ arg ;
110+ continue ;
111+ }
112+
113+ if ($ this ->shouldSkipArg ($ arg , $ parameter )) {
114+ continue ;
115+ }
116+
117+ $ arg ->name = new Identifier (name: $ parameter ->getName ());
118+ $ namedArgs [] = $ arg ;
96119 }
97120
98- foreach ($ node ->args as $ index => $ arg ) {
99- $ arg ->name = $ argNames [$ index ];
121+ $ node ->args = $ namedArgs ;
122+ }
123+
124+ private function shouldSkipArg (Arg $ arg , ExtendedParameterReflection $ parameter ): bool
125+ {
126+ try {
127+ $ defaultValue = $ parameter ->getDefaultValue ();
128+ } catch (\Throwable ) {
129+ return false ;
130+ }
131+
132+ try {
133+ $ argValue = $ this ->constExprEvaluator ->evaluateDirectly ($ arg ->value );
134+ } catch (\Throwable ) {
135+ return false ;
136+ }
137+
138+ if ($ defaultValue instanceof \PHPStan \Type \ConstantScalarType) {
139+ $ defaultValue = $ defaultValue ->getValue ();
100140 }
141+ return $ argValue === $ defaultValue ;
101142 }
102143
103144 public function provideMinPhpVersion (): int
0 commit comments