From 5eb0bb8126886b0d7c8322462add8ed00a475ff1 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 20 Jan 2019 11:49:13 -0500 Subject: [PATCH] Make foreach var annotation use more cautious --- .../Statements/Block/ForeachAnalyzer.php | 31 ++++++++++++++ tests/AnnotationTest.php | 42 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 637d4fb1a..c2f2b6ce3 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -66,11 +66,42 @@ class ForeachAnalyzer } } + $safe_var_ids = []; + + if ($stmt->keyVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->keyVar->name)) { + $safe_var_ids['$' . $stmt->keyVar->name] = true; + } + + if ($stmt->valueVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->valueVar->name)) { + $safe_var_ids['$' . $stmt->valueVar->name] = true; + } elseif ($stmt->valueVar instanceof PhpParser\Node\Expr\List_) { + foreach ($stmt->valueVar->items as $list_item) { + if (!$list_item) { + continue; + } + + $list_item_key = $list_item->key; + $list_item_value = $list_item->value; + + if ($list_item_value instanceof PhpParser\Node\Expr\Variable && is_string($list_item_value->name)) { + $safe_var_ids['$' . $list_item_value->name] = true; + } + + if ($list_item_key instanceof PhpParser\Node\Expr\Variable && is_string($list_item_key->name)) { + $safe_var_ids['$' . $list_item_key->name] = true; + } + } + } + foreach ($var_comments as $var_comment) { if (!$var_comment->var_id) { continue; } + if (isset($safe_var_ids[$var_comment->var_id])) { + continue; + } + $comment_type = ExpressionAnalyzer::fleshOutType( $codebase, $var_comment->type, diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 092fa9631..5e0eeec46 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -797,6 +797,48 @@ class AnnotationTest extends TestCase echo strlen($a);', ], + 'annotationOnForeachItems' => [ + ' $_) {} + + if (is_null($item)) {} + } + + function bat(array $arr) : void { + $item = null; + + /** @var string $item */ + foreach ($arr as list($item)) {} + + if (is_null($item)) {} + } + + function baz(array $arr) : void { + $item = null; + + /** @var string $item */ + foreach ($arr as list($item => $_)) {} + + if (is_null($item)) {} + }', + [], + [ + 'MixedAssignment' + ] + ], ]; }