Skip to content

Commit c958367

Browse files
committed
fix anonymous class detection
1 parent 13b1067 commit c958367

File tree

5 files changed

+51
-17
lines changed

5 files changed

+51
-17
lines changed

Diff for: src/Parser/VariadicMethodsVisitor.php

+5-6
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ final class VariadicMethodsVisitor extends NodeVisitorAbstract
3131
/** @var array<string, array<string, TrinaryLogic>> */
3232
private array $variadicMethods = [];
3333

34-
private int $anonymousClassIndex = 0;
35-
3634
public const ATTRIBUTE_NAME = 'variadicMethods';
3735

3836
public function beforeTraverse(array $nodes): ?array
@@ -43,7 +41,6 @@ public function beforeTraverse(array $nodes): ?array
4341
$this->classStack = [];
4442
$this->inClassLike = null;
4543
$this->inMethod = null;
46-
$this->anonymousClassIndex = 0;
4744

4845
return null;
4946
}
@@ -60,13 +57,15 @@ public function enterNode(Node $node): ?Node
6057

6158
if ($node instanceof Node\Stmt\ClassLike) {
6259
if (!$node->name instanceof Node\Identifier) {
63-
$className = sprintf('class@anonymous:%s:%s', $node->getStartLine(), ++$this->anonymousClassIndex);
60+
$className = sprintf('class@anonymous:%s:%s', $node->getStartLine(), $node->getEndLine());
61+
$this->classStack[] = $className;
62+
$this->inClassLike = $className; // anonymous classes are in global namespace
6463
} else {
6564
$className = $node->name->name;
65+
$this->classStack[] = $className;
66+
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
6667
}
6768

68-
$this->classStack[] = $className;
69-
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
7069
$this->variadicMethods[$this->inClassLike] ??= [];
7170
}
7271

Diff for: src/Reflection/Php/PhpMethodReflection.php

+9-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use function explode;
3636
use function in_array;
3737
use function is_array;
38+
use function sprintf;
3839
use function strtolower;
3940
use const PHP_VERSION_ID;
4041

@@ -253,12 +254,17 @@ private function isVariadic(): bool
253254
if (count($nodes) > 0) {
254255
$variadicMethods = $nodes[0]->getAttribute(VariadicMethodsVisitor::ATTRIBUTE_NAME);
255256

257+
$className = $declaringClass->getName();
258+
if ($declaringClass->isAnonymous()) {
259+
$className = sprintf('class@anonymous:%s:%s', $declaringClass->getNativeReflection()->getStartLine(), $declaringClass->getNativeReflection()->getEndLine());
260+
}
261+
256262
if (
257263
is_array($variadicMethods)
258-
&& array_key_exists($declaringClass->getName(), $variadicMethods)
259-
&& array_key_exists($this->reflection->getName(), $variadicMethods[$declaringClass->getName()])
264+
&& array_key_exists($className, $variadicMethods)
265+
&& array_key_exists($this->reflection->getName(), $variadicMethods[$className])
260266
) {
261-
return $this->containsVariadicCalls = !$variadicMethods[$declaringClass->getName()][$this->reflection->getName()]->no();
267+
return $this->containsVariadicCalls = !$variadicMethods[$className][$this->reflection->getName()]->no();
262268
}
263269
}
264270

Diff for: tests/PHPStan/Parser/ParserTest.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,28 @@ public function dataVariadicCallLikes(): iterable
3131
[
3232
'VariadicMethod\X' => [
3333
'non_variadic_fn1' => TrinaryLogic::createNo(),
34-
'variadic_fn1' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
34+
'variadic_fn1' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
3535
'implicit_variadic_fn1' => TrinaryLogic::createYes(),
3636
],
3737
'VariadicMethod\Z' => [
3838
'non_variadic_fnZ' => TrinaryLogic::createNo(),
39-
'variadic_fnZ' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
39+
'variadic_fnZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
4040
'implicit_variadic_fnZ' => TrinaryLogic::createYes(),
4141
],
42-
'VariadicMethod\Z\class@anonymous:20:1' => [
42+
'class@anonymous:20:30' => [
4343
'non_variadic_fn_subZ' => TrinaryLogic::createNo(),
44-
'variadic_fn_subZ' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
44+
'variadic_fn_subZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
4545
'implicit_variadic_subZ' => TrinaryLogic::createYes(),
4646
],
47-
'VariadicMethod\class@anonymous:42:2' => [
47+
'class@anonymous:42:52' => [
4848
'non_variadic_fn' => TrinaryLogic::createNo(),
49-
'variadic_fn' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
49+
'variadic_fn' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
5050
'implicit_variadic_fn' => TrinaryLogic::createYes(),
5151
],
52-
'VariadicMethod\class@anonymous:54:3' => [
52+
'class@anonymous:54:58' => [
5353
'implicit_variadic_fn' => TrinaryLogic::createYes(),
5454
],
55-
'VariadicMethod\class@anonymous:54:4' => [],
55+
'class@anonymous:54:54' => [],
5656
],
5757
];
5858

Diff for: tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -3412,4 +3412,19 @@ public function testBug1953(): void
34123412
]);
34133413
}
34143414

3415+
public function testBug11559c(): void
3416+
{
3417+
$this->checkThisOnly = false;
3418+
$this->checkNullables = true;
3419+
$this->checkUnionTypes = true;
3420+
$this->checkExplicitMixed = true;
3421+
3422+
$this->analyse([__DIR__ . '/data/bug-11559c.php'], [
3423+
[
3424+
'Method class@anonymous/tests/PHPStan/Rules/Methods/data/bug-11559c.php:5:1::regular_fn() invoked with 3 parameters, 1 required.',
3425+
14,
3426+
],
3427+
]);
3428+
}
3429+
34153430
}

Diff for: tests/PHPStan/Rules/Methods/data/bug-11559c.php

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Bug11559c;
4+
5+
$c = new class (new class {}) {
6+
function implicit_variadic_fn() {
7+
$args = func_get_args();
8+
}
9+
function regular_fn(int $i) {
10+
}
11+
};
12+
13+
$c->implicit_variadic_fn(1, 2, 3);
14+
$c->regular_fn(1, 2, 3);

0 commit comments

Comments
 (0)