Skip to content

Commit 8c871fc

Browse files
authored
Narrow args to list wherever possible
1 parent 46c2519 commit 8c871fc

File tree

10 files changed

+222
-15
lines changed

10 files changed

+222
-15
lines changed

build/phpstan.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ parameters:
124124
- stubs/ReactStreams.stub
125125
- stubs/NetteDIContainer.stub
126126
- stubs/PhpParserName.stub
127+
- stubs/PhpParserNode.stub
128+
- stubs/PhpParserExpr.stub
129+
- stubs/PhpParserStmt.stub
127130
- stubs/Identifier.stub
128131

129132
rules:

build/stubs/PhpParserExpr.stub

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace PhpParser\Node\Expr;
4+
5+
use PhpParser\Node;
6+
use PhpParser\NodeAbstract;
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\Expr;
9+
use PhpParser\Node\Identifier;
10+
use PhpParser\Node\VariadicPlaceholder;
11+
use PhpParser\Node\FunctionLike;
12+
13+
abstract class CallLike extends Expr {
14+
/**
15+
* Return raw arguments, which may be actual Args, or VariadicPlaceholders for first-class
16+
* callables.
17+
*
18+
* @return list<Arg|VariadicPlaceholder>
19+
*/
20+
abstract public function getRawArgs(): array;
21+
/**
22+
* Assert that this is not a first-class callable and return only ordinary Args.
23+
*
24+
* @return list<Arg>
25+
*/
26+
public function getArgs(): array {
27+
assert(!$this->isFirstClassCallable());
28+
return $this->getRawArgs();
29+
}
30+
}
31+
32+
class FuncCall extends CallLike {
33+
/** @var list<Node\Arg|Node\VariadicPlaceholder> Arguments */
34+
public array $args;
35+
36+
/**
37+
* Constructs a function call node.
38+
*
39+
* @param Node\Name|Expr $name Function name
40+
* @param list<Node\Arg|Node\VariadicPlaceholder> $args Arguments
41+
* @param array<string, mixed> $attributes Additional attributes
42+
*/
43+
public function __construct(Node $name, array $args = [], array $attributes = []) {}
44+
}
45+
46+
class MethodCall extends CallLike {
47+
/** @var list<Node\Arg|Node\VariadicPlaceholder> Arguments */
48+
public array $args;
49+
50+
/**
51+
* Constructs a function call node.
52+
*
53+
* @param Expr $var Variable holding object
54+
* @param string|Identifier|Expr $name Method name
55+
* @param list<Arg|VariadicPlaceholder> $args Arguments
56+
* @param array<string, mixed> $attributes Additional attributes
57+
*/
58+
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {}
59+
}
60+
61+
class New_ extends CallLike {
62+
/** @var list<Arg|VariadicPlaceholder> Arguments */
63+
public array $args;
64+
65+
/**
66+
* Constructs a function call node.
67+
*
68+
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
69+
* @param list<Arg|VariadicPlaceholder> $args Arguments
70+
* @param array<string, mixed> $attributes Additional attributes
71+
*/
72+
public function __construct(Node $class, array $args = [], array $attributes = []) {}
73+
}
74+
75+
class NullsafeMethodCall extends CallLike {
76+
/** @var list<Arg|VariadicPlaceholder> Arguments */
77+
public array $args;
78+
79+
/**
80+
* Constructs a nullsafe method call node.
81+
*
82+
* @param Expr $var Variable holding object
83+
* @param string|Identifier|Expr $name Method name
84+
* @param list<Arg|VariadicPlaceholder> $args Arguments
85+
* @param array<string, mixed> $attributes Additional attributes
86+
*/
87+
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {}
88+
}
89+
90+
class StaticCall extends CallLike {
91+
/** @var list<Arg|VariadicPlaceholder> Arguments */
92+
public array $args;
93+
94+
/**
95+
* Constructs a static method call node.
96+
*
97+
* @param Node\Name|Expr $class Class name
98+
* @param string|Identifier|Expr $name Method name
99+
* @param list<Arg|VariadicPlaceholder> $args Arguments
100+
* @param array<string, mixed> $attributes Additional attributes
101+
*/
102+
public function __construct(Node $class, $name, array $args = [], array $attributes = []) {}
103+
}
104+
105+
class Closure extends Expr implements FunctionLike {
106+
/** @var list<Node\Stmt> Statements */
107+
public array $stmts;
108+
}

build/stubs/PhpParserNode.stub

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace PhpParser\Node;
4+
5+
use PhpParser\NodeAbstract;
6+
7+
class PropertyHook extends NodeAbstract implements FunctionLike {
8+
/** @var null|Expr|list<Stmt> Hook body */
9+
public $body;
10+
}

build/stubs/PhpParserStmt.stub

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace PhpParser\Node\Stmt;
4+
5+
use PhpParser\Modifiers;
6+
use PhpParser\Node;
7+
use PhpParser\Node\FunctionLike;
8+
9+
class Block extends Node\Stmt {
10+
/** @var list<Node\Stmt> Statements */
11+
public array $stmts;
12+
}
13+
14+
class Case_ extends Node\Stmt {
15+
/** @var list<Node\Stmt> Statements */
16+
public array $stmts;
17+
}
18+
19+
class Catch_ extends Node\Stmt {
20+
/** @var list<Node\Stmt> Statements */
21+
public array $stmts;
22+
}
23+
24+
class Do_ extends Node\Stmt {
25+
/** @var list<Node\Stmt> Statements */
26+
public array $stmts;
27+
}
28+
29+
class ElseIf_ extends Node\Stmt {
30+
/** @var list<Node\Stmt> Statements */
31+
public array $stmts;
32+
}
33+
34+
class Else_ extends Node\Stmt {
35+
/** @var list<Node\Stmt> Statements */
36+
public array $stmts;
37+
}
38+
39+
class Finally_ extends Node\Stmt {
40+
/** @var list<Node\Stmt> Statements */
41+
public array $stmts;
42+
}
43+
44+
class For_ extends Node\Stmt {
45+
/** @var list<Node\Stmt> Statements */
46+
public array $stmts;
47+
}
48+
49+
class Foreach_ extends Node\Stmt {
50+
/** @var list<Node\Stmt> Statements */
51+
public array $stmts;
52+
}
53+
54+
class If_ extends Node\Stmt {
55+
/** @var list<Node\Stmt> Statements */
56+
public array $stmts;
57+
}
58+
59+
class Namespace_ extends Node\Stmt {
60+
/** @var list<Node\Stmt> Statements */
61+
public array $stmts;
62+
}
63+
64+
class TryCatch extends Node\Stmt {
65+
/** @var list<Node\Stmt> Statements */
66+
public array $stmts;
67+
}
68+
69+
class While_ extends Node\Stmt {
70+
/** @var list<Node\Stmt> Statements */
71+
public array $stmts;
72+
}
73+
74+
class ClassMethod extends Node\Stmt implements FunctionLike {
75+
/** @var list<Node\Stmt>|null Statements */
76+
public ?array $stmts;
77+
}
78+
79+
class Function_ extends Node\Stmt implements FunctionLike {
80+
/** @var list<Node\Stmt> Statements */
81+
public array $stmts;
82+
}
83+

src/Analyser/ArgumentsNormalizer.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
use PHPStan\ShouldNotHappenException;
1414
use PHPStan\TrinaryLogic;
1515
use PHPStan\Type\Constant\ConstantArrayType;
16+
use function array_is_list;
1617
use function array_key_exists;
1718
use function array_keys;
19+
use function array_values;
1820
use function count;
1921
use function ksort;
2022
use function max;
@@ -182,8 +184,8 @@ public static function reorderNewArguments(
182184
}
183185

184186
/**
185-
* @param Arg[] $callArgs
186-
* @return ?array<int, Arg>
187+
* @param list<Arg> $callArgs
188+
* @return ?list<Arg>
187189
*/
188190
public static function reorderArgs(ParametersAcceptor $parametersAcceptor, array $callArgs): ?array
189191
{
@@ -322,6 +324,10 @@ public static function reorderArgs(ParametersAcceptor $parametersAcceptor, array
322324
$reorderedArgs[] = $arg;
323325
}
324326

327+
if (!array_is_list($reorderedArgs)) {
328+
$reorderedArgs = array_values($reorderedArgs);
329+
}
330+
325331
return $reorderedArgs;
326332
}
327333

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4455,7 +4455,7 @@ private function getMethodThrowPoint(MethodReflection $methodReflection, Paramet
44554455
}
44564456

44574457
/**
4458-
* @param Node\Arg[] $args
4458+
* @param list<Node\Arg> $args
44594459
*/
44604460
private function getConstructorThrowPoint(MethodReflection $constructorReflection, ParametersAcceptor $parametersAcceptor, ClassReflection $classReflection, New_ $new, Name $className, array $args, MutatingScope $scope): ?ThrowPoint
44614461
{

src/Parser/CleaningVisitor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ public function enterNode(Node $node): ?Node
5050
}
5151

5252
/**
53-
* @param Node\Stmt[] $stmts
54-
* @return Node\Stmt[]
53+
* @param list<Node\Stmt|Node\Expr> $stmts
54+
* @return list<Node\Stmt>
5555
*/
5656
private function keepVariadicsAndYields(array $stmts, ?string $hookedPropertyName): array
5757
{

src/Reflection/ParametersAcceptorSelector.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
use PHPStan\Type\TypeCombinator;
4444
use PHPStan\Type\TypeTraverser;
4545
use PHPStan\Type\UnionType;
46+
use function array_is_list;
4647
use function array_key_exists;
4748
use function array_key_last;
4849
use function array_map;
@@ -471,6 +472,11 @@ public static function selectFromArgs(
471472
$parameters = null;
472473
$singleParametersAcceptor = null;
473474
if (count($parametersAcceptors) === 1) {
475+
if (!array_is_list($args)) {
476+
// actually $args parameter should be typed to list but we can't atm,
477+
// because its a BC break.
478+
$args = array_values($args);
479+
}
474480
$reorderedArgs = ArgumentsNormalizer::reorderArgs($parametersAcceptors[0], $args);
475481
$singleParametersAcceptor = $parametersAcceptors[0];
476482
}

src/Rules/Functions/RandomIntParametersRule.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use PHPStan\Type\Constant\ConstantIntegerType;
1515
use PHPStan\Type\IntegerRangeType;
1616
use PHPStan\Type\VerbosityLevel;
17-
use function array_values;
1817
use function count;
1918
use function sprintf;
2019

@@ -49,7 +48,7 @@ public function processNode(Node $node, Scope $scope): array
4948
return [];
5049
}
5150

52-
$args = array_values($node->getArgs());
51+
$args = $node->getArgs();
5352
if (count($args) < 2) {
5453
return [];
5554
}

tests/PHPStan/Analyser/ArgumentsNormalizerLegacyTest.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,9 @@ public function testArgumentReorderAllNamed(): void
4949
$reorderedArgs = $funcCall->getArgs();
5050
$this->assertCount(2, $reorderedArgs);
5151

52-
$this->assertArrayHasKey(0, $reorderedArgs);
5352
$this->assertNull($reorderedArgs[0]->name, 'named-arg turned into regular numeric arg');
5453
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at the right position');
5554

56-
$this->assertArrayHasKey(1, $reorderedArgs);
5755
$this->assertNull($reorderedArgs[1]->name, 'named-arg turned into regular numeric arg');
5856
$this->assertInstanceOf(LNumber::class, $reorderedArgs[1]->value, 'flags-arg at the right position');
5957
$this->assertSame(0, $reorderedArgs[1]->value->value);
@@ -90,17 +88,14 @@ public function testArgumentReorderAllNamedWithSkipped(): void
9088
$reorderedArgs = $funcCall->getArgs();
9189
$this->assertCount(3, $reorderedArgs);
9290

93-
$this->assertArrayHasKey(0, $reorderedArgs);
9491
$this->assertNull($reorderedArgs[0]->name, 'named-arg turned into regular numeric arg');
9592
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at the right position');
9693

97-
$this->assertArrayHasKey(1, $reorderedArgs);
9894
$this->assertNull($reorderedArgs[1]->name, 'named-arg turned into regular numeric arg');
9995
$this->assertInstanceOf(TypeExpr::class, $reorderedArgs[1]->value, 'flags-arg at the right position');
10096
$this->assertInstanceOf(ConstantIntegerType::class, $reorderedArgs[1]->value->getExprType());
10197
$this->assertSame(0, $reorderedArgs[1]->value->getExprType()->getValue(), 'flags-arg with default value');
10298

103-
$this->assertArrayHasKey(2, $reorderedArgs);
10499
$this->assertNull($reorderedArgs[2]->name, 'named-arg turned into regular numeric arg');
105100
$this->assertInstanceOf(LNumber::class, $reorderedArgs[2]->value, 'depth-arg at the right position');
106101
$this->assertSame(128, $reorderedArgs[2]->value->value);
@@ -153,10 +148,7 @@ public function testLeaveRegularCallAsIs(): void
153148
$reorderedArgs = $funcCall->getArgs();
154149
$this->assertCount(2, $reorderedArgs);
155150

156-
$this->assertArrayHasKey(0, $reorderedArgs);
157151
$this->assertInstanceOf(String_::class, $reorderedArgs[0]->value, 'value-arg at unchanged position');
158-
159-
$this->assertArrayHasKey(1, $reorderedArgs);
160152
$this->assertInstanceOf(LNumber::class, $reorderedArgs[1]->value, 'flags-arg at unchanged position');
161153
$this->assertSame(0, $reorderedArgs[1]->value->value);
162154
}

0 commit comments

Comments
 (0)