diff --git a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test --- a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test +++ b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test @@ -179,6 +179,12 @@ echo $z; }; } + +function some_func($x, $y) { + $func = function ($z) use ($x) { + echo "$x/$y/$z"; + }; +} ~~~~~~~~~~ warning:9:3 error:28:3 @@ -201,3 +207,4 @@ error:164:9 error:171:5 error:178:10 +error:185:14 diff --git a/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php --- a/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php @@ -68,6 +68,7 @@ ) + array_fill_keys($this->getSuperGlobalNames(), 0); $declaration_tokens = array(); $exclude_tokens = array(); + $exclude_strings = array(); $vars = array(); // First up, find all the different kinds of declarations, as explained @@ -175,6 +176,16 @@ foreach ($func_decl->selectDescendantsOfType('n_VARIABLE') as $var) { $exclude_tokens[$var->getID()] = true; } + + foreach (array('n_STRING_SCALAR', 'n_HEREDOC') as $type) { + foreach ($func_decl->selectDescendantsOfType($type) as $string) { + $exclude_strings[$string->getID()] = array(); + + foreach ($string->getStringVariables() as $offset => $var) { + $exclude_strings[$string->getID()][$var] = true; + } + } + } } // Now we have every declaration except foreach(), handled below. Build @@ -316,6 +327,10 @@ foreach (array('n_STRING_SCALAR', 'n_HEREDOC') as $type) { foreach ($body->selectDescendantsOfType($type) as $string) { foreach ($string->getStringVariables() as $offset => $var) { + if (isset($exclude_strings[$string->getID()][$var])) { + continue; + } + $all[$string->getOffset() + $offset - 1] = '$'.$var; } }