diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 140dc7e02b..64d0dd9ec1 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -117,7 +117,6 @@ use PHPStan\Node\UnreachableStatementNode; use PHPStan\Node\VariableAssignNode; use PHPStan\Node\VarTagChangedExpressionTypeNode; -use PHPStan\Parser\AnonymousClassVisitor; use PHPStan\Parser\ArrowFunctionArgVisitor; use PHPStan\Parser\ClosureArgVisitor; use PHPStan\Parser\Parser; @@ -856,7 +855,7 @@ private function processStmtNode( if ($stmt->name === null) { throw new ShouldNotHappenException(); } - if ($stmt->getAttribute(AnonymousClassVisitor::ATTRIBUTE_ANONYMOUS_CLASS, false) === false) { + if (!$stmt->isAnonymous()) { $classReflection = $this->reflectionProvider->getClass($stmt->name->toString()); } else { $classReflection = $this->reflectionProvider->getAnonymousClassReflection($stmt, $scope); diff --git a/src/Node/AnonymousClassNode.php b/src/Node/AnonymousClassNode.php new file mode 100644 index 0000000000..b5d8333496 --- /dev/null +++ b/src/Node/AnonymousClassNode.php @@ -0,0 +1,32 @@ +getSubNodeNames() as $subNodeName) { + $subNodes[$subNodeName] = $node->$subNodeName; + } + + return new AnonymousClassNode( + $node->name, + $subNodes, + $node->getAttributes(), + ); + } + + public function isAnonymous(): bool + { + return true; + } + +} diff --git a/src/Parser/AnonymousClassVisitor.php b/src/Parser/AnonymousClassVisitor.php index 3a179206ba..33ca16c567 100644 --- a/src/Parser/AnonymousClassVisitor.php +++ b/src/Parser/AnonymousClassVisitor.php @@ -4,15 +4,15 @@ use PhpParser\Node; use PhpParser\NodeVisitorAbstract; +use PHPStan\Node\AnonymousClassNode; use function count; class AnonymousClassVisitor extends NodeVisitorAbstract { - public const ATTRIBUTE_ANONYMOUS_CLASS = 'anonymousClass'; public const ATTRIBUTE_LINE_INDEX = 'anonymousClassLineIndex'; - /** @var array> */ + /** @var array> */ private array $nodesPerLine = []; public function beforeTraverse(array $nodes): ?array @@ -27,10 +27,11 @@ public function enterNode(Node $node): ?Node return null; } - $node->setAttribute(self::ATTRIBUTE_ANONYMOUS_CLASS, true); + $node = AnonymousClassNode::createFromClassNode($node); + $node->setAttribute('anonymousClass', true); // We keep this for backward compatibility $this->nodesPerLine[$node->getStartLine()][] = $node; - return null; + return $node; } public function afterTraverse(array $nodes): ?array diff --git a/src/Type/FileTypeMapper.php b/src/Type/FileTypeMapper.php index 07df40e471..3e0c73a93e 100644 --- a/src/Type/FileTypeMapper.php +++ b/src/Type/FileTypeMapper.php @@ -8,7 +8,6 @@ use PHPStan\BetterReflection\Util\GetLastDocComment; use PHPStan\Broker\AnonymousClassNameHelper; use PHPStan\File\FileHelper; -use PHPStan\Parser\AnonymousClassVisitor; use PHPStan\Parser\Parser; use PHPStan\PhpDoc\PhpDocNodeResolver; use PHPStan\PhpDoc\PhpDocStringResolver; @@ -261,7 +260,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA } $className = $this->anonymousClassNameHelper->getAnonymousClassName($node, $fileName); - } elseif ((bool) $node->getAttribute(AnonymousClassVisitor::ATTRIBUTE_ANONYMOUS_CLASS, false)) { + } elseif ($node instanceof Node\Stmt\Class_ && $node->isAnonymous()) { $className = $node->name->name; } else { if ($traitFound) { @@ -452,7 +451,7 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun } $className = $this->anonymousClassNameHelper->getAnonymousClassName($node, $fileName); - } elseif ((bool) $node->getAttribute(AnonymousClassVisitor::ATTRIBUTE_ANONYMOUS_CLASS, false)) { + } elseif ($node instanceof Node\Stmt\Class_ && $node->isAnonymous()) { $className = $node->name->name; } else { if ($traitFound) { diff --git a/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php b/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php index 6a07961d4e..9310f617f1 100644 --- a/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php +++ b/tests/PHPStan/Reflection/AnonymousClassReflectionTest.php @@ -5,10 +5,10 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PHPStan\Analyser\Scope; -use PHPStan\Parser\AnonymousClassVisitor; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Assert; use function implode; use function sprintf; @@ -36,10 +36,12 @@ public function getNodeType(): string public function processNode(Node $node, Scope $scope): array { - if (!(bool) $node->getAttribute(AnonymousClassVisitor::ATTRIBUTE_ANONYMOUS_CLASS)) { + if (!$node->isAnonymous()) { return []; } + Assert::assertTrue($node->getAttribute('anonymousClass')); + $classReflection = $this->reflectionProvider->getAnonymousClassReflection($node, $scope); return [