diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 991646c768..d562027865 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -343,6 +343,7 @@ private function processStmtNode( } elseif ( !$stmt instanceof Static_ && !$stmt instanceof Foreach_ + && !$stmt instanceof Node\Stmt\Global_ ) { $scope = $this->processStmtVarAnnotation($scope, $stmt, null); } @@ -1152,6 +1153,7 @@ private function processStmtNode( } } elseif ($stmt instanceof Node\Stmt\Global_) { $hasYield = false; + $vars = []; foreach ($stmt->vars as $var) { if (!$var instanceof Variable) { throw new \PHPStan\ShouldNotHappenException(); @@ -1165,6 +1167,11 @@ private function processStmtNode( } $scope = $scope->assignVariable($var->name, new MixedType()); + $vars[] = $var->name; + } + $comment = CommentHelper::getDocComment($stmt); + if ($comment !== null) { + $scope = $this->processVarAnnotation($scope, $vars, $comment); } } elseif ($stmt instanceof Static_) { $hasYield = false; diff --git a/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php b/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php index 418f10b5ba..77e332f5ad 100644 --- a/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php +++ b/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php @@ -73,6 +73,10 @@ public function processNode(Node $node, Scope $scope): array return $this->processStmt($scope, $varTags, $node->expr); } + if ($node instanceof Node\Stmt\Global_) { + return $this->processGlobal($scope, $node, $varTags); + } + return $this->processStmt($scope, $varTags, null); } @@ -285,4 +289,52 @@ private function processStmt(Scope $scope, array $varTags, ?Expr $defaultExpr): return $errors; } + /** + * @param \PHPStan\Analyser\Scope $scope + * @param \PHPStan\PhpDoc\Tag\VarTag[] $varTags + * @return \PHPStan\Rules\RuleError[] + */ + private function processGlobal(Scope $scope, Node\Stmt\Global_ $node, array $varTags): array + { + $variableNames = []; + foreach ($node->vars as $var) { + if (!$var instanceof Expr\Variable) { + continue; + } + if (!is_string($var->name)) { + continue; + } + + $variableNames[$var->name] = true; + } + + $errors = []; + foreach (array_keys($varTags) as $name) { + if (is_int($name)) { + if (count($variableNames) === 1) { + continue; + } + + $errors[] = RuleErrorBuilder::message( + 'PHPDoc tag @var above multiple global variables does not specify variable name.' + )->build(); + continue; + } + + if (isset($variableNames[$name])) { + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf( + 'Variable $%s in PHPDoc tag @var does not match any global variable: %s', + $name, + implode(', ', array_map(static function (string $name): string { + return sprintf('$%s', $name); + }, array_keys($variableNames))) + ))->build(); + } + + return $errors; + } + } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index aca1eeba3e..4a9ac97417 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -10753,6 +10753,11 @@ public function dataCompact(): array return $this->gatherAssertTypes(__DIR__ . '/data/compact.php'); } + public function dataBug4500(): array + { + return $this->gatherAssertTypes(__DIR__ . '/data/bug-4500.php'); + } + /** * @param string $file * @return array @@ -10966,6 +10971,7 @@ private function gatherAssertTypes(string $file): array * @dataProvider dataBug4398 * @dataProvider dataBug4415 * @dataProvider dataCompact + * @dataProvider dataBug4500 * @param string $assertType * @param string $file * @param mixed ...$args diff --git a/tests/PHPStan/Analyser/data/bug-4500.php b/tests/PHPStan/Analyser/data/bug-4500.php new file mode 100644 index 0000000000..7a928b5eef --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4500.php @@ -0,0 +1,74 @@ +analyse([__DIR__ . '/data/bug-3515.php'], []); } + public function testBug4500(): void + { + $this->analyse([__DIR__ . '/data/bug-4500.php'], [ + [ + 'PHPDoc tag @var above multiple global variables does not specify variable name.', + 23, + ], + [ + 'Variable $baz in PHPDoc tag @var does not match any global variable: $lorem', + 43, + ], + [ + 'Variable $baz in PHPDoc tag @var does not match any global variable: $lorem', + 49, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/PhpDoc/data/bug-4500.php b/tests/PHPStan/Rules/PhpDoc/data/bug-4500.php new file mode 100644 index 0000000000..bdf832d565 --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/data/bug-4500.php @@ -0,0 +1,52 @@ +