From 3d365fc7af8833656b6e58c1b37d3a8b17079173 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 8 Feb 2021 12:30:38 +0100 Subject: [PATCH] WrongVariableNameInVarTagRule - fix iteratee variable above foreach --- src/Analyser/NodeScopeResolver.php | 3 +++ .../PhpDoc/WrongVariableNameInVarTagRule.php | 7 +++++-- .../Analyser/NodeScopeResolverTest.php | 8 ++++++- tests/PHPStan/Analyser/data/bug-4504.php | 21 +++++++++++++++++++ .../WrongVariableNameInVarTagRuleTest.php | 15 ++++++++----- tests/PHPStan/Rules/PhpDoc/data/bug-4504.php | 16 ++++++++++++++ .../PhpDoc/data/wrong-variable-name-var.php | 2 +- 7 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-4504.php create mode 100644 tests/PHPStan/Rules/PhpDoc/data/bug-4504.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index f1e9909dd4..868f77f1f6 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -2897,6 +2897,9 @@ private function processVarAnnotation(MutatingScope $scope, array $variableNames private function enterForeach(MutatingScope $scope, Foreach_ $stmt): MutatingScope { + if ($stmt->expr instanceof Variable && is_string($stmt->expr->name)) { + $scope = $this->processVarAnnotation($scope, [$stmt->expr->name], $stmt); + } $iterateeType = $scope->getType($stmt->expr); $vars = []; if ($stmt->valueVar instanceof Variable && is_string($stmt->valueVar->name)) { diff --git a/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php b/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php index 88a8107ef6..558b105992 100644 --- a/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php +++ b/src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php @@ -62,7 +62,7 @@ public function processNode(Node $node, Scope $scope): array } if ($node instanceof Node\Stmt\Foreach_) { - return $this->processForeach($node->keyVar, $node->valueVar, $varTags); + return $this->processForeach($node->expr, $node->keyVar, $node->valueVar, $varTags); } if ($node instanceof Node\Stmt\Static_) { @@ -168,9 +168,12 @@ private function getAssignedVariables(Expr $expr): array * @param \PHPStan\PhpDoc\Tag\VarTag[] $varTags * @return \PHPStan\Rules\RuleError[] */ - private function processForeach(?Node\Expr $keyVar, Node\Expr $valueVar, array $varTags): array + private function processForeach(Node\Expr $iterateeExpr, ?Node\Expr $keyVar, Node\Expr $valueVar, array $varTags): array { $variableNames = []; + if ($iterateeExpr instanceof Node\Expr\Variable && is_string($iterateeExpr->name)) { + $variableNames[] = $iterateeExpr->name; + } if ($keyVar instanceof Node\Expr\Variable && is_string($keyVar->name)) { $variableNames[] = $keyVar->name; } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 4a9ac97417..ffc4bbe546 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -4776,7 +4776,7 @@ public function dataForeachArrayType(): array ], [ __DIR__ . '/data/foreach/type-in-comment-no-variable-2.php', - 'mixed', + '*ERROR*', '$value', ], [ @@ -10758,6 +10758,11 @@ public function dataBug4500(): array return $this->gatherAssertTypes(__DIR__ . '/data/bug-4500.php'); } + public function dataBug4504(): array + { + return $this->gatherAssertTypes(__DIR__ . '/data/bug-4504.php'); + } + /** * @param string $file * @return array @@ -10972,6 +10977,7 @@ private function gatherAssertTypes(string $file): array * @dataProvider dataBug4415 * @dataProvider dataCompact * @dataProvider dataBug4500 + * @dataProvider dataBug4504 * @param string $assertType * @param string $file * @param mixed ...$args diff --git a/tests/PHPStan/Analyser/data/bug-4504.php b/tests/PHPStan/Analyser/data/bug-4504.php new file mode 100644 index 0000000000..8ebb18c171 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4504.php @@ -0,0 +1,21 @@ + $models */ + foreach ($models as $k => $v) { + assertType('Bug4504TypeInference\A', $v); + } + + assertType('array()|Iterator', $models); + } + +} + diff --git a/tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php b/tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php index c3a276d7a2..62f62306a0 100644 --- a/tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php @@ -31,11 +31,7 @@ public function testRule(): void 23, ], [ - 'Variable $list in PHPDoc tag @var does not match any variable in the foreach loop: $key, $var', - 29, - ], - [ - 'Variable $foo in PHPDoc tag @var does not match any variable in the foreach loop: $key, $val', + 'Variable $foo in PHPDoc tag @var does not match any variable in the foreach loop: $list, $key, $val', 66, ], [ @@ -86,6 +82,10 @@ public function testRule(): void 'Variable $foo in PHPDoc tag @var does not exist.', 210, ], + [ + 'PHPDoc tag @var above foreach loop does not specify variable name.', + 234, + ], [ 'Variable $foo in PHPDoc tag @var does not exist.', 248, @@ -151,4 +151,9 @@ public function testBug4500(): void ]); } + public function testBug4504(): void + { + $this->analyse([__DIR__ . '/data/bug-4504.php'], []); + } + } diff --git a/tests/PHPStan/Rules/PhpDoc/data/bug-4504.php b/tests/PHPStan/Rules/PhpDoc/data/bug-4504.php new file mode 100644 index 0000000000..dc6a7b2745 --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/data/bug-4504.php @@ -0,0 +1,16 @@ + $models */ + foreach ($models as $k => $v) { + + } + } + +} diff --git a/tests/PHPStan/Rules/PhpDoc/data/wrong-variable-name-var.php b/tests/PHPStan/Rules/PhpDoc/data/wrong-variable-name-var.php index c87e4e4dbd..4ece594f48 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/wrong-variable-name-var.php +++ b/tests/PHPStan/Rules/PhpDoc/data/wrong-variable-name-var.php @@ -26,7 +26,7 @@ public function doFoo() public function doBar(array $list) { /** @var int[] $list */ - foreach ($list as $key => $var) { // ERROR + foreach ($list as $key => $var) { }