Skip to content

Commit

Permalink
fix anonymous class detection
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Oct 8, 2024
1 parent 86f7d0a commit 08ca712
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 17 deletions.
11 changes: 5 additions & 6 deletions src/Parser/VariadicMethodsVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ final class VariadicMethodsVisitor extends NodeVisitorAbstract
/** @var array<string, array<string, TrinaryLogic>> */
private array $variadicMethods = [];

private int $anonymousClassIndex = 0;

public const ATTRIBUTE_NAME = 'variadicMethods';

public function beforeTraverse(array $nodes): ?array
Expand All @@ -43,7 +41,6 @@ public function beforeTraverse(array $nodes): ?array
$this->classStack = [];
$this->inClassLike = null;
$this->inMethod = null;
$this->anonymousClassIndex = 0;

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

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

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

Expand Down
12 changes: 9 additions & 3 deletions src/Reflection/Php/PhpMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use function explode;
use function in_array;
use function is_array;
use function sprintf;
use function strtolower;
use const PHP_VERSION_ID;

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

$className = $declaringClass->getName();
if ($declaringClass->isAnonymous()) {
$className = sprintf('class@anonymous:%s:%s', $declaringClass->getNativeReflection()->getStartLine(), $declaringClass->getNativeReflection()->getEndLine());
}

if (
is_array($variadicMethods)
&& array_key_exists($declaringClass->getName(), $variadicMethods)
&& array_key_exists($this->reflection->getName(), $variadicMethods[$declaringClass->getName()])
&& array_key_exists($className, $variadicMethods)
&& array_key_exists($this->reflection->getName(), $variadicMethods[$className])
) {
return $this->containsVariadicCalls = !$variadicMethods[$declaringClass->getName()][$this->reflection->getName()]->no();
return $this->containsVariadicCalls = !$variadicMethods[$className][$this->reflection->getName()]->no();
}
}

Expand Down
16 changes: 8 additions & 8 deletions tests/PHPStan/Parser/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ public function dataVariadicCallLikes(): iterable
[
'VariadicMethod\X' => [
'non_variadic_fn1' => TrinaryLogic::createNo(),
'variadic_fn1' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
'variadic_fn1' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
'implicit_variadic_fn1' => TrinaryLogic::createYes(),
],
'VariadicMethod\Z' => [
'non_variadic_fnZ' => TrinaryLogic::createNo(),
'variadic_fnZ' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
'variadic_fnZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
'implicit_variadic_fnZ' => TrinaryLogic::createYes(),
],
'VariadicMethod\Z\class@anonymous:20:1' => [
'class@anonymous:20:30' => [
'non_variadic_fn_subZ' => TrinaryLogic::createNo(),
'variadic_fn_subZ' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
'variadic_fn_subZ' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
'implicit_variadic_subZ' => TrinaryLogic::createYes(),
],
'VariadicMethod\class@anonymous:42:2' => [
'class@anonymous:42:52' => [
'non_variadic_fn' => TrinaryLogic::createNo(),
'variadic_fn' => TrinaryLogic::createNo(), // variadicness later on detected via reflection
'variadic_fn' => TrinaryLogic::createNo(), // native variadicness later on detected via reflection
'implicit_variadic_fn' => TrinaryLogic::createYes(),
],
'VariadicMethod\class@anonymous:54:3' => [
'class@anonymous:54:58' => [
'implicit_variadic_fn' => TrinaryLogic::createYes(),
],
'VariadicMethod\class@anonymous:54:4' => [],
'class@anonymous:54:54' => [],
],
];

Expand Down
15 changes: 15 additions & 0 deletions tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3412,4 +3412,19 @@ public function testBug1953(): void
]);
}

public function testBug11559c(): void
{
$this->checkThisOnly = false;
$this->checkNullables = true;
$this->checkUnionTypes = true;
$this->checkExplicitMixed = true;

$this->analyse([__DIR__ . '/data/bug-11559c.php'], [
[
'Method class@anonymous/tests/PHPStan/Rules/Methods/data/bug-11559c.php:5:1::regular_fn() invoked with 3 parameters, 1 required.',
14,
],
]);
}

}
14 changes: 14 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-11559c.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Bug11559c;

$c = new class (new class {}) {
function implicit_variadic_fn() {
$args = func_get_args();
}
function regular_fn(int $i) {
}
};

$c->implicit_variadic_fn(1, 2, 3);
$c->regular_fn(1, 2, 3);

0 comments on commit 08ca712

Please sign in to comment.