Skip to content

Commit d4d5c1a

Browse files
committed
My vision
1 parent 676d440 commit d4d5c1a

File tree

5 files changed

+48
-93
lines changed

5 files changed

+48
-93
lines changed

src/Parser/VariadicFunctionsVisitor.php

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use PhpParser\Node\Name;
77
use PhpParser\NodeVisitorAbstract;
88
use PHPStan\Reflection\ParametersAcceptor;
9-
use PHPStan\TrinaryLogic;
9+
use function array_filter;
1010
use function array_key_exists;
1111
use function in_array;
1212

@@ -19,7 +19,7 @@ final class VariadicFunctionsVisitor extends NodeVisitorAbstract
1919

2020
private ?string $inFunction = null;
2121

22-
/** @var array<string, TrinaryLogic> */
22+
/** @var array<string, bool> */
2323
private array $variadicFunctions = [];
2424

2525
public const ATTRIBUTE_NAME = 'variadicFunctions';
@@ -46,31 +46,16 @@ public function enterNode(Node $node): ?Node
4646

4747
if ($node instanceof Node\Stmt\Function_) {
4848
$this->inFunction = $this->inNamespace !== null ? $this->inNamespace . '\\' . $node->name->name : $node->name->name;
49-
50-
foreach ($node->params as $parameter) {
51-
if (!$parameter->variadic) {
52-
continue;
53-
}
54-
55-
if (!array_key_exists($this->inFunction, $this->variadicFunctions)) {
56-
$this->variadicFunctions[$this->inFunction] = TrinaryLogic::createYes();
57-
} else {
58-
$this->variadicFunctions[$this->inFunction]->and(TrinaryLogic::createYes());
59-
}
60-
}
6149
}
6250

6351
if (
6452
$this->inFunction !== null
6553
&& $node instanceof Node\Expr\FuncCall
6654
&& $node->name instanceof Name
6755
&& in_array((string) $node->name, ParametersAcceptor::VARIADIC_FUNCTIONS, true)
56+
&& !array_key_exists($this->inFunction, $this->variadicFunctions)
6857
) {
69-
if (!array_key_exists($this->inFunction, $this->variadicFunctions)) {
70-
$this->variadicFunctions[$this->inFunction] = TrinaryLogic::createYes();
71-
} else {
72-
$this->variadicFunctions[$this->inFunction]->and(TrinaryLogic::createYes());
73-
}
58+
$this->variadicFunctions[$this->inFunction] = true;
7459
}
7560

7661
return null;
@@ -83,7 +68,7 @@ public function leaveNode(Node $node): ?Node
8368
}
8469

8570
if ($node instanceof Node\Stmt\Function_ && $this->inFunction !== null) {
86-
$this->variadicFunctions[$this->inFunction] ??= TrinaryLogic::createNo();
71+
$this->variadicFunctions[$this->inFunction] ??= false;
8772
$this->inFunction = null;
8873
}
8974

@@ -93,7 +78,8 @@ public function leaveNode(Node $node): ?Node
9378
public function afterTraverse(array $nodes): ?array
9479
{
9580
if ($this->topNode !== null && $this->variadicFunctions !== []) {
96-
$this->topNode->setAttribute(self::ATTRIBUTE_NAME, $this->variadicFunctions);
81+
$functions = array_filter($this->variadicFunctions, static fn (bool $variadic) => $variadic);
82+
$this->topNode->setAttribute(self::ATTRIBUTE_NAME, $functions);
9783
}
9884

9985
return null;

src/Parser/VariadicMethodsVisitor.php

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,37 @@
77
use PhpParser\Node\Stmt\ClassMethod;
88
use PhpParser\NodeVisitorAbstract;
99
use PHPStan\Reflection\ParametersAcceptor;
10-
use PHPStan\TrinaryLogic;
1110
use function array_key_exists;
1211
use function array_pop;
1312
use function count;
14-
use function implode;
1513
use function in_array;
1614
use function sprintf;
17-
use function str_contains;
1815

1916
final class VariadicMethodsVisitor extends NodeVisitorAbstract
2017
{
2118

19+
public const ATTRIBUTE_NAME = 'variadicMethods';
20+
21+
public const ANONYMOUS_CLASS_PREFIX = 'class@anonymous';
22+
2223
private ?Node $topNode = null;
2324

2425
private ?string $inNamespace = null;
2526

26-
private ?string $inClassLike = null;
27-
2827
/** @var array<string> */
2928
private array $classStack = [];
3029

3130
private ?string $inMethod = null;
3231

33-
/** @var array<string, array<string, TrinaryLogic>> */
32+
/** @var array<string, array<string, true>> */
3433
private array $variadicMethods = [];
3534

36-
public const ATTRIBUTE_NAME = 'variadicMethods';
37-
38-
private const ANONYMOUS_CLASS_PREFIX = 'class@anonymous';
39-
4035
public function beforeTraverse(array $nodes): ?array
4136
{
4237
$this->topNode = null;
4338
$this->variadicMethods = [];
4439
$this->inNamespace = null;
4540
$this->classStack = [];
46-
$this->inClassLike = null;
4741
$this->inMethod = null;
4842

4943
return null;
@@ -63,31 +57,30 @@ public function enterNode(Node $node): ?Node
6357
if (!$node->name instanceof Node\Identifier) {
6458
$className = sprintf('%s:%s:%s', self::ANONYMOUS_CLASS_PREFIX, $node->getStartLine(), $node->getEndLine());
6559
$this->classStack[] = $className;
66-
$this->inClassLike = $className; // anonymous classes are in global namespace
6760
} else {
6861
$className = $node->name->name;
69-
$this->classStack[] = $className;
70-
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
62+
$this->classStack[] = $this->inNamespace !== null ? $this->inNamespace . '\\' . $className : $className;
7163
}
72-
73-
$this->variadicMethods[$this->inClassLike] ??= [];
7464
}
7565

76-
if ($this->inClassLike !== null && $node instanceof ClassMethod) {
66+
if ($node instanceof ClassMethod) {
7767
$this->inMethod = $node->name->name;
7868
}
7969

8070
if (
81-
$this->inClassLike !== null
82-
&& $this->inMethod !== null
71+
$this->inMethod !== null
8372
&& $node instanceof Node\Expr\FuncCall
8473
&& $node->name instanceof Name
8574
&& in_array((string) $node->name, ParametersAcceptor::VARIADIC_FUNCTIONS, true)
8675
) {
87-
if (!array_key_exists($this->inMethod, $this->variadicMethods[$this->inClassLike])) {
88-
$this->variadicMethods[$this->inClassLike][$this->inMethod] = TrinaryLogic::createYes();
89-
} else {
90-
$this->variadicMethods[$this->inClassLike][$this->inMethod]->and(TrinaryLogic::createYes());
76+
$lastClass = $this->classStack[count($this->classStack) - 1] ?? null;
77+
if ($lastClass !== null) {
78+
if (
79+
!array_key_exists($lastClass, $this->variadicMethods)
80+
|| !array_key_exists($this->inMethod, $this->variadicMethods[$lastClass])
81+
) {
82+
$this->variadicMethods[$lastClass][$this->inMethod] = true;
83+
}
9184
}
9285

9386
}
@@ -97,28 +90,12 @@ public function enterNode(Node $node): ?Node
9790

9891
public function leaveNode(Node $node): ?Node
9992
{
100-
if (
101-
$node instanceof ClassMethod
102-
&& $this->inClassLike !== null
103-
) {
104-
$this->variadicMethods[$this->inClassLike][$node->name->name] ??= TrinaryLogic::createNo();
93+
if ($node instanceof ClassMethod) {
10594
$this->inMethod = null;
10695
}
10796

10897
if ($node instanceof Node\Stmt\ClassLike) {
10998
array_pop($this->classStack);
110-
111-
if ($this->classStack !== []) {
112-
$lastClass = $this->classStack[count($this->classStack) - 1];
113-
114-
if (str_contains($lastClass, self::ANONYMOUS_CLASS_PREFIX)) {
115-
$this->inClassLike = $lastClass;
116-
} else {
117-
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
118-
}
119-
} else {
120-
$this->inClassLike = null;
121-
}
12299
}
123100

124101
if ($node instanceof Node\Stmt\Namespace_ && $node->name !== null) {

src/Reflection/Php/PhpFunctionReflection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private function isVariadic(): bool
148148
is_array($variadicFunctions)
149149
&& array_key_exists($this->reflection->getName(), $variadicFunctions)
150150
) {
151-
return $this->containsVariadicCalls = !$variadicFunctions[$this->reflection->getName()]->no();
151+
return $this->containsVariadicCalls = $variadicFunctions[$this->reflection->getName()];
152152
}
153153
}
154154

src/Reflection/Php/PhpMethodReflection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,15 @@ private function isVariadic(): bool
256256

257257
$className = $declaringClass->getName();
258258
if ($declaringClass->isAnonymous()) {
259-
$className = sprintf('class@anonymous:%s:%s', $declaringClass->getNativeReflection()->getStartLine(), $declaringClass->getNativeReflection()->getEndLine());
259+
$className = sprintf('%s:%s:%s', VariadicMethodsVisitor::ANONYMOUS_CLASS_PREFIX, $declaringClass->getNativeReflection()->getStartLine(), $declaringClass->getNativeReflection()->getEndLine());
260260
}
261261

262262
if (
263263
is_array($variadicMethods)
264264
&& array_key_exists($className, $variadicMethods)
265265
&& array_key_exists($this->reflection->getName(), $variadicMethods[$className])
266266
) {
267-
return $this->containsVariadicCalls = !$variadicMethods[$className][$this->reflection->getName()]->no();
267+
return $this->containsVariadicCalls = $variadicMethods[$className][$this->reflection->getName()];
268268
}
269269
}
270270

tests/PHPStan/Parser/ParserTest.php

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace PHPStan\Parser;
44

55
use PHPStan\Testing\PHPStanTestCase;
6-
use PHPStan\TrinaryLogic;
6+
use function count;
77

88
/**
99
* @covers \PHPStan\Parser\RichParser
@@ -18,10 +18,7 @@ public function dataVariadicCallLikes(): iterable
1818
__DIR__ . '/data/variadic-functions.php',
1919
VariadicFunctionsVisitor::ATTRIBUTE_NAME,
2020
[
21-
'VariadicFunctions\variadic_fn1' => TrinaryLogic::createYes(),
22-
'VariadicFunctions\nonvariadic' => TrinaryLogic::createNo(),
23-
'VariadicFunctions\maybe_variadic_fn1' => TrinaryLogic::createNo(),
24-
'VariadicFunctions\implicit_variadic_fn1' => TrinaryLogic::createYes(),
21+
'VariadicFunctions\implicit_variadic_fn1' => true,
2522
],
2623
];
2724

@@ -30,34 +27,23 @@ public function dataVariadicCallLikes(): iterable
3027
VariadicMethodsVisitor::ATTRIBUTE_NAME,
3128
[
3229
'VariadicMethod\X' => [
33-
'non_variadic_fn1' => TrinaryLogic::createNo(),
34-
'variadic_fn1' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
35-
'implicit_variadic_fn1' => TrinaryLogic::createYes(),
30+
'implicit_variadic_fn1' => true,
3631
],
3732
'VariadicMethod\Z' => [
38-
'non_variadic_fnZ' => TrinaryLogic::createNo(),
39-
'variadic_fnZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
40-
'implicit_variadic_fnZ' => TrinaryLogic::createYes(),
33+
'implicit_variadic_fnZ' => true,
4134
],
4235
'class@anonymous:20:30' => [
43-
'non_variadic_fn_subZ' => TrinaryLogic::createNo(),
44-
'variadic_fn_subZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
45-
'implicit_variadic_subZ' => TrinaryLogic::createYes(),
36+
'implicit_variadic_subZ' => true,
4637
],
4738
'class@anonymous:42:52' => [
48-
'non_variadic_fn' => TrinaryLogic::createNo(),
49-
'variadic_fn' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
50-
'implicit_variadic_fn' => TrinaryLogic::createYes(),
39+
'implicit_variadic_fn' => true,
5140
],
5241
'class@anonymous:54:58' => [
53-
'implicit_variadic_fn' => TrinaryLogic::createYes(),
42+
'implicit_variadic_fn' => true,
5443
],
55-
'class@anonymous:54:54' => [],
5644
'class@anonymous:61:68' => [
57-
'nestedClass' => TrinaryLogic::createNo(),
58-
'implicit_variadic_fn' => TrinaryLogic::createYes(),
45+
'implicit_variadic_fn' => true,
5946
],
60-
'class@anonymous:63:63' => [],
6147
],
6248
];
6349

@@ -66,17 +52,15 @@ public function dataVariadicCallLikes(): iterable
6652
VariadicMethodsVisitor::ATTRIBUTE_NAME,
6753
[
6854
'VariadicMethodEnum\X' => [
69-
'non_variadic_fn1' => TrinaryLogic::createNo(),
70-
'variadic_fn1' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
71-
'implicit_variadic_fn1' => TrinaryLogic::createYes(),
55+
'implicit_variadic_fn1' => true,
7256
],
7357
],
7458
];
7559
}
7660

7761
/**
7862
* @dataProvider dataVariadicCallLikes
79-
* @param array<string, TrinaryLogic>|array<string, array<string, TrinaryLogic>> $expectedVariadics
63+
* @param array<string, true>|array<string, array<string, true>> $expectedVariadics
8064
* @throws ParserErrorsException
8165
*/
8266
public function testSimpleParserVariadicCallLikes(string $file, string $attributeName, array $expectedVariadics): void
@@ -86,12 +70,16 @@ public function testSimpleParserVariadicCallLikes(string $file, string $attribut
8670
$ast = $parser->parseFile($file);
8771
$variadics = $ast[0]->getAttribute($attributeName);
8872
$this->assertIsArray($variadics);
89-
$this->assertSame($expectedVariadics, $variadics);
73+
$this->assertCount(count($expectedVariadics), $variadics);
74+
foreach ($expectedVariadics as $key => $expectedVariadic) {
75+
$this->assertArrayHasKey($key, $variadics);
76+
$this->assertSame($expectedVariadic, $variadics[$key]);
77+
}
9078
}
9179

9280
/**
9381
* @dataProvider dataVariadicCallLikes
94-
* @param array<string, TrinaryLogic>|array<string, array<string, TrinaryLogic>> $expectedVariadics
82+
* @param array<string, true>|array<string, array<string, true>> $expectedVariadics
9583
* @throws ParserErrorsException
9684
*/
9785
public function testRichParserVariadicCallLikes(string $file, string $attributeName, array $expectedVariadics): void
@@ -101,7 +89,11 @@ public function testRichParserVariadicCallLikes(string $file, string $attributeN
10189
$ast = $parser->parseFile($file);
10290
$variadics = $ast[0]->getAttribute($attributeName);
10391
$this->assertIsArray($variadics);
104-
$this->assertSame($expectedVariadics, $variadics);
92+
$this->assertCount(count($expectedVariadics), $variadics);
93+
foreach ($expectedVariadics as $key => $expectedVariadic) {
94+
$this->assertArrayHasKey($key, $variadics);
95+
$this->assertSame($expectedVariadic, $variadics[$key]);
96+
}
10597
}
10698

10799
}

0 commit comments

Comments
 (0)