diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index eae4f4eb67..efa23eb051 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -2912,7 +2912,7 @@ public function getAssignedVariables(Expr $expr): array */ public function callNodeCallbackWithExpression( callable $nodeCallback, - Expr $expr, + Node $expr, MutatingScope $scope, ExpressionResultStorage $storage, ExpressionContext $context, diff --git a/src/Node/CoalesceExpressionNode.php b/src/Node/CoalesceExpressionNode.php index e8653bbc42..fc5e1ff494 100644 --- a/src/Node/CoalesceExpressionNode.php +++ b/src/Node/CoalesceExpressionNode.php @@ -4,6 +4,7 @@ use Override; use PhpParser\Node\Expr; +use PhpParser\NodeAbstract; use PHPStan\Analyser\ExpressionResult; /** @@ -14,7 +15,7 @@ * * @internal */ -final class CoalesceExpressionNode extends Expr implements VirtualNode +final class CoalesceExpressionNode extends NodeAbstract implements VirtualNode { public function __construct( diff --git a/src/Node/EmptyExpressionNode.php b/src/Node/EmptyExpressionNode.php index e6685101a7..66ae18a4d9 100644 --- a/src/Node/EmptyExpressionNode.php +++ b/src/Node/EmptyExpressionNode.php @@ -3,8 +3,8 @@ namespace PHPStan\Node; use Override; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\Empty_; +use PhpParser\NodeAbstract; use PHPStan\Analyser\ExpressionResult; /** @@ -14,7 +14,7 @@ * * @internal */ -final class EmptyExpressionNode extends Expr implements VirtualNode +final class EmptyExpressionNode extends NodeAbstract implements VirtualNode { public function __construct(Empty_ $originalNode, private ExpressionResult $exprResult) diff --git a/src/Node/IssetExpressionNode.php b/src/Node/IssetExpressionNode.php index 6c8bc87019..640093aaf2 100644 --- a/src/Node/IssetExpressionNode.php +++ b/src/Node/IssetExpressionNode.php @@ -3,8 +3,8 @@ namespace PHPStan\Node; use Override; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\Isset_; +use PhpParser\NodeAbstract; use PHPStan\Analyser\ExpressionResult; /** @@ -14,7 +14,7 @@ * * @internal */ -final class IssetExpressionNode extends Expr implements VirtualNode +final class IssetExpressionNode extends NodeAbstract implements VirtualNode { /** diff --git a/tests/PHPStan/Analyser/GetTypeOnEveryNodeRuleTest.php b/tests/PHPStan/Analyser/GetTypeOnEveryNodeRuleTest.php new file mode 100644 index 0000000000..db38c9d2d9 --- /dev/null +++ b/tests/PHPStan/Analyser/GetTypeOnEveryNodeRuleTest.php @@ -0,0 +1,48 @@ +getType() on every + * expression they receive. Virtual nodes emitted to node callbacks must + * therefore either not be expressions at all or be resolvable and printable - + * an isset()/empty()/?? statement used to emit Expr-extending virtual nodes + * with no printer method, crashing the type-cache key printer. + * + * @extends RuleTestCase> + */ +class GetTypeOnEveryNodeRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new class implements Rule { + + public function getNodeType(): string + { + return Node::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ($node instanceof Expr) { + $scope->getType($node); + } + + return []; + } + + }; + } + + public function testGetTypeOnEveryNode(): void + { + $this->analyse([__DIR__ . '/data/get-type-on-every-node.php'], []); + } + +} diff --git a/tests/PHPStan/Analyser/data/get-type-on-every-node.php b/tests/PHPStan/Analyser/data/get-type-on-every-node.php new file mode 100644 index 0000000000..1c8a2de6a8 --- /dev/null +++ b/tests/PHPStan/Analyser/data/get-type-on-every-node.php @@ -0,0 +1,27 @@ + */ + private array $data = []; + + public function sayHello(?string $name): string + { + if (isset($this->data[$name])) { + return $this->data[$name]; + } + + if (empty($this->data)) { + return 'empty'; + } + + $fallback = $name ?? 'anonymous'; + $this->data[$fallback] ??= 'hello'; + + return $this->data[$fallback]; + } + +}