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 @@ -172,6 +172,13 @@ } } +function some_func($x, $y) { + $func = function($z) use ($x) { + echo $x; + echo $y; + echo $z; + }; +} ~~~~~~~~~~ error:28:3 error:30:3 @@ -191,3 +198,4 @@ error:150:9 error:164:9 error:171:5 +error:178:10 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 @@ -164,6 +164,17 @@ $scope_destroyed_at = min($scope_destroyed_at, $call->getOffset()); } + $func_decls = $body->selectDescendantsOfType('n_FUNCTION_DECLARATION'); + foreach ($func_decls as $func_decl) { + if ($func_decl->getChildByIndex(2)->getTypeName() != 'n_EMPTY') { + continue; + } + + foreach ($func_decl->selectDescendantsOfType('n_VARIABLE') as $var) { + $exclude_tokens[$var->getID()] = true; + } + } + // Now we have every declaration except foreach(), handled below. Build // two maps, one which just keeps track of which tokens are part of // declarations ($declaration_tokens) and one which has the first offset