mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 23:02:41 +01:00
Lint undeclared variables in strings
Summary: Depends on D6405. Test Plan: New unit test. Linted libphutil, Arcanist, Phabricator. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D6406
This commit is contained in:
parent
fbcd686e18
commit
f5ceceea87
2 changed files with 29 additions and 11 deletions
|
@ -1100,6 +1100,9 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
// because the function calls extract() or uses dynamic variables,
|
// because the function calls extract() or uses dynamic variables,
|
||||||
// preventing us from keeping track of which variables are defined) so we
|
// preventing us from keeping track of which variables are defined) so we
|
||||||
// can stop issuing warnings after that.
|
// can stop issuing warnings after that.
|
||||||
|
//
|
||||||
|
// TODO: Support functions defined inside other functions which is commonly
|
||||||
|
// used with anonymous functions.
|
||||||
|
|
||||||
$fdefs = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
|
$fdefs = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
|
||||||
$mdefs = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
|
$mdefs = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
|
||||||
|
@ -1232,7 +1235,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
$declaration_tokens[$var->getID()] = true;
|
$declaration_tokens[$var->getID()] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Excluded tokens are ones we don't "count" as being uses, described
|
// Excluded tokens are ones we don't "count" as being used, described
|
||||||
// above. Put them into $exclude_tokens.
|
// above. Put them into $exclude_tokens.
|
||||||
|
|
||||||
$class_statics = $body
|
$class_statics = $body
|
||||||
|
@ -1260,7 +1263,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
// declared before a loop after the loop, even if you're just assigning
|
// declared before a loop after the loop, even if you're just assigning
|
||||||
// to it.
|
// to it.
|
||||||
|
|
||||||
$concrete = $var->getConcreteString();
|
$concrete = $this->getConcreteVariableString($var);
|
||||||
$uses[$concrete][$var->getID()] = $var->getOffset();
|
$uses[$concrete][$var->getID()] = $var->getOffset();
|
||||||
|
|
||||||
if (isset($declaration_tokens[$var->getID()])) {
|
if (isset($declaration_tokens[$var->getID()])) {
|
||||||
|
@ -1272,7 +1275,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$all[$var->getID()] = $var;
|
$all[$var->getOffset()] = $concrete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1361,7 +1364,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
|
|
||||||
// This is a declaration, exclude it from the "declare variables prior
|
// This is a declaration, exclude it from the "declare variables prior
|
||||||
// to use" check below.
|
// to use" check below.
|
||||||
unset($all[$var->getID()]);
|
unset($all[$var->getOffset()]);
|
||||||
|
|
||||||
$vars[] = $var;
|
$vars[] = $var;
|
||||||
}
|
}
|
||||||
|
@ -1376,21 +1379,28 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
$declaration_tokens[$var->getID()] = true;
|
$declaration_tokens[$var->getID()] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (array('n_STRING_SCALAR', 'n_HEREDOC') as $type) {
|
||||||
|
foreach ($body->selectDescendantsOfType($type) as $string) {
|
||||||
|
foreach ($string->getStringVariables() as $offset => $var) {
|
||||||
|
$all[$string->getOffset() + $offset - 1] = '$'.$var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Issue a warning for every variable token, unless it appears in a
|
// Issue a warning for every variable token, unless it appears in a
|
||||||
// declaration, we know about a prior declaration, we have explicitly
|
// declaration, we know about a prior declaration, we have explicitly
|
||||||
// exlcuded it, or scope has been made unknowable before it appears.
|
// exlcuded it, or scope has been made unknowable before it appears.
|
||||||
|
|
||||||
$issued_warnings = array();
|
$issued_warnings = array();
|
||||||
foreach ($all as $id => $var) {
|
foreach ($all as $offset => $concrete) {
|
||||||
if ($var->getOffset() >= $scope_destroyed_at) {
|
if ($offset >= $scope_destroyed_at) {
|
||||||
// This appears after an extract() or $$var so we have no idea
|
// This appears after an extract() or $$var so we have no idea
|
||||||
// whether it's legitimate or not. We raised a harshly-worded warning
|
// whether it's legitimate or not. We raised a harshly-worded warning
|
||||||
// when scope was made unknowable, so just ignore anything we can't
|
// when scope was made unknowable, so just ignore anything we can't
|
||||||
// figure out.
|
// figure out.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$concrete = $this->getConcreteVariableString($var);
|
if ($offset >= idx($declarations, $concrete, PHP_INT_MAX)) {
|
||||||
if ($var->getOffset() >= idx($declarations, $concrete, PHP_INT_MAX)) {
|
|
||||||
// The use appears after the variable is declared, so it's fine.
|
// The use appears after the variable is declared, so it's fine.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1399,12 +1409,13 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
|
||||||
// to issue another one.
|
// to issue another one.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$this->raiseLintAtNode(
|
$this->raiseLintAtOffset(
|
||||||
$var,
|
$offset,
|
||||||
self::LINT_UNDECLARED_VARIABLE,
|
self::LINT_UNDECLARED_VARIABLE,
|
||||||
'Declare variables prior to use (even if you are passing them '.
|
'Declare variables prior to use (even if you are passing them '.
|
||||||
'as reference parameters). You may have misspelled this '.
|
'as reference parameters). You may have misspelled this '.
|
||||||
'variable name.');
|
'variable name.',
|
||||||
|
$concrete);
|
||||||
$issued_warnings[$concrete] = true;
|
$issued_warnings[$concrete] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,12 @@ function arrow($o, $x) {
|
||||||
echo $o->{$x->{$x->{$x.$x->{$x}}.$x}};
|
echo $o->{$x->{$x->{$x.$x->{$x}}.$x}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function strings() {
|
||||||
|
$a = 1;
|
||||||
|
echo "$a";
|
||||||
|
echo "$b";
|
||||||
|
}
|
||||||
|
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
disabled:3:1
|
disabled:3:1
|
||||||
error:30:3
|
error:30:3
|
||||||
|
@ -178,3 +184,4 @@ error:121:3 isset() and empty() should not trigger errors.
|
||||||
error:125:3 Should only warn once in this function.
|
error:125:3 Should only warn once in this function.
|
||||||
error:146:8
|
error:146:8
|
||||||
error:152:9
|
error:152:9
|
||||||
|
error:166:9
|
||||||
|
|
Loading…
Reference in a new issue