diff --git a/src/lint/linter/xhpast/rules/ArcanistAbstractMethodBodyXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistAbstractMethodBodyXHPASTLinterRule.php index 277e15e0..8c37ab43 100644 --- a/src/lint/linter/xhpast/rules/ArcanistAbstractMethodBodyXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistAbstractMethodBodyXHPASTLinterRule.php @@ -1,30 +1,30 @@ selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $modifiers = $this->getModifiers($method); - $body = $method->getChildByIndex(5); + $body = $method->getChildByIndex(6); if (idx($modifiers, 'abstract') && $body->getTypeName() != 'n_EMPTY') { $this->raiseLintAtNode( $body, pht( '`%s` methods cannot contain a body. This construct will '. 'cause a fatal error.', 'abstract')); } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistInterfaceMethodBodyXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistInterfaceMethodBodyXHPASTLinterRule.php index f4835d0b..bf230be0 100644 --- a/src/lint/linter/xhpast/rules/ArcanistInterfaceMethodBodyXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistInterfaceMethodBodyXHPASTLinterRule.php @@ -1,33 +1,33 @@ selectDescendantsOfType('n_INTERFACE_DECLARATION'); foreach ($interfaces as $interface) { $methods = $interface->selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { - $body = $method->getChildByIndex(5); + $body = $method->getChildByIndex(6); if ($body->getTypeName() != 'n_EMPTY') { $this->raiseLintAtNode( $body, pht( '`%s` methods cannot contain a body. This construct will '. 'cause a fatal error.', 'interface')); } } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php index 182edc69..a1a4cc52 100644 --- a/src/lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php @@ -1,277 +1,277 @@ selectDescendantsOfTypes(array( 'n_FUNCTION_DECLARATION', 'n_METHOD_DECLARATION', )); foreach ($defs as $def) { // We keep track of the first offset where scope becomes unknowable, and // silence any warnings after that. Default it to INT_MAX so we can min() // it later to keep track of the first problem we encounter. $scope_destroyed_at = PHP_INT_MAX; $declarations = array( '$this' => 0, ) + array_fill_keys($this->getSuperGlobalNames(), 0); $declaration_tokens = array(); $exclude_tokens = array(); $vars = array(); // First up, find all the different kinds of declarations, as explained // above. Put the tokens into the $vars array. $param_list = $def->getChildOfType(3, 'n_DECLARATION_PARAMETER_LIST'); $param_vars = $param_list->selectDescendantsOfType('n_VARIABLE'); foreach ($param_vars as $var) { $vars[] = $var; } // This is PHP5.3 closure syntax: function () use ($x) {}; $lexical_vars = $def ->getChildByIndex(4) ->selectDescendantsOfType('n_VARIABLE'); foreach ($lexical_vars as $var) { $vars[] = $var; } - $body = $def->getChildByIndex(5); + $body = $def->getChildByIndex(6); if ($body->getTypeName() === 'n_EMPTY') { // Abstract method declaration. continue; } $static_vars = $body ->selectDescendantsOfType('n_STATIC_DECLARATION') ->selectDescendantsOfType('n_VARIABLE'); foreach ($static_vars as $var) { $vars[] = $var; } $global_vars = $body ->selectDescendantsOfType('n_GLOBAL_DECLARATION_LIST'); foreach ($global_vars as $var_list) { foreach ($var_list->getChildren() as $var) { if ($var->getTypeName() === 'n_VARIABLE') { $vars[] = $var; } else { // Dynamic global variable, i.e. "global $$x;". $scope_destroyed_at = min($scope_destroyed_at, $var->getOffset()); // An error is raised elsewhere, no need to raise here. } } } // Include "catch (Exception $ex)", but not variables in the body of the // catch block. $catches = $body->selectDescendantsOfType('n_CATCH'); foreach ($catches as $catch) { $vars[] = $catch->getChildOfType(1, 'n_VARIABLE'); } $binary = $body->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($binary as $expr) { if ($expr->getChildByIndex(1)->getConcreteString() !== '=') { continue; } $lval = $expr->getChildByIndex(0); if ($lval->getTypeName() === 'n_VARIABLE') { $vars[] = $lval; } else if ($lval->getTypeName() === 'n_LIST') { // Recursivey grab everything out of list(), since the grammar // permits list() to be nested. Also note that list() is ONLY valid // as an lval assignments, so we could safely lift this out of the // n_BINARY_EXPRESSION branch. $assign_vars = $lval->selectDescendantsOfType('n_VARIABLE'); foreach ($assign_vars as $var) { $vars[] = $var; } } if ($lval->getTypeName() === 'n_VARIABLE_VARIABLE') { $scope_destroyed_at = min($scope_destroyed_at, $lval->getOffset()); // No need to raise here since we raise an error elsewhere. } } $calls = $body->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($calls as $call) { $name = strtolower($call->getChildByIndex(0)->getConcreteString()); if ($name === 'empty' || $name === 'isset') { $params = $call ->getChildOfType(1, 'n_CALL_PARAMETER_LIST') ->selectDescendantsOfType('n_VARIABLE'); foreach ($params as $var) { $exclude_tokens[$var->getID()] = true; } continue; } if ($name !== 'extract') { continue; } $scope_destroyed_at = min($scope_destroyed_at, $call->getOffset()); } // 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 // where a variable is declared ($declarations). foreach ($vars as $var) { $concrete = $this->getConcreteVariableString($var); $declarations[$concrete] = min( idx($declarations, $concrete, PHP_INT_MAX), $var->getOffset()); $declaration_tokens[$var->getID()] = true; } // Excluded tokens are ones we don't "count" as being used, described // above. Put them into $exclude_tokens. $class_statics = $body ->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); $class_static_vars = $class_statics ->selectDescendantsOfType('n_VARIABLE'); foreach ($class_static_vars as $var) { $exclude_tokens[$var->getID()] = true; } // Find all the variables in scope, and figure out where they are used. // We want to find foreach() iterators which are both declared before and // used after the foreach() loop. $uses = array(); $all_vars = $body->selectDescendantsOfType('n_VARIABLE'); $all = array(); // NOTE: $all_vars is not a real array so we can't unset() it. foreach ($all_vars as $var) { // Be strict since it's easier; we don't let you reuse an iterator you // declared before a loop after the loop, even if you're just assigning // to it. $concrete = $this->getConcreteVariableString($var); $uses[$concrete][$var->getID()] = $var->getOffset(); if (isset($declaration_tokens[$var->getID()])) { // We know this is part of a declaration, so it's fine. continue; } if (isset($exclude_tokens[$var->getID()])) { // We know this is part of isset() or similar, so it's fine. continue; } $all[$var->getOffset()] = $concrete; } // Do foreach() last, we want to handle implicit redeclaration of a // variable already in scope since this probably means we're ovewriting a // local. // NOTE: Processing foreach expressions in order allows programs which // reuse iterator variables in other foreach() loops -- this is fine. We // have a separate warning to prevent nested loops from reusing the same // iterators. $foreaches = $body->selectDescendantsOfType('n_FOREACH'); $all_foreach_vars = array(); foreach ($foreaches as $foreach) { $foreach_expr = $foreach->getChildOfType(0, 'n_FOREACH_EXPRESSION'); $foreach_vars = array(); // Determine the end of the foreach() loop. $foreach_tokens = $foreach->getTokens(); $last_token = end($foreach_tokens); $foreach_end = $last_token->getOffset(); $key_var = $foreach_expr->getChildByIndex(1); if ($key_var->getTypeName() === 'n_VARIABLE') { $foreach_vars[] = $key_var; } $value_var = $foreach_expr->getChildByIndex(2); if ($value_var->getTypeName() === 'n_VARIABLE') { $foreach_vars[] = $value_var; } else { // The root-level token may be a reference, as in: // foreach ($a as $b => &$c) { ... } // Reach into the n_VARIABLE_REFERENCE node to grab the n_VARIABLE // node. $var = $value_var->getChildByIndex(0); if ($var->getTypeName() === 'n_VARIABLE_VARIABLE') { $var = $var->getChildByIndex(0); } $foreach_vars[] = $var; } // Remove all uses of the iterators inside of the foreach() loop from // the $uses map. foreach ($foreach_vars as $var) { $concrete = $this->getConcreteVariableString($var); $offset = $var->getOffset(); foreach ($uses[$concrete] as $id => $use_offset) { if (($use_offset >= $offset) && ($use_offset < $foreach_end)) { unset($uses[$concrete][$id]); } } $all_foreach_vars[] = $var; } } foreach ($all_foreach_vars as $var) { $concrete = $this->getConcreteVariableString($var); $offset = $var->getOffset(); // If a variable was declared before a foreach() and is used after // it, raise a message. if (isset($declarations[$concrete])) { if ($declarations[$concrete] < $offset) { if (!empty($uses[$concrete]) && max($uses[$concrete]) > $offset) { $message = $this->raiseLintAtNode( $var, pht( 'This iterator variable is a previously declared local '. 'variable. To avoid overwriting locals, do not reuse them '. 'as iterator variables.')); $message->setOtherLocations(array( $this->getOtherLocation($declarations[$concrete]), $this->getOtherLocation(max($uses[$concrete])), )); } } } // This is a declaration, exclude it from the "declare variables prior // to use" check below. unset($all[$var->getOffset()]); $vars[] = $var; } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php index 65851435..fc6b8a6b 100644 --- a/src/lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistReusedIteratorReferenceXHPASTLinterRule.php @@ -1,185 +1,185 @@ selectDescendantsOfTypes(array( 'n_FUNCTION_DECLARATION', 'n_METHOD_DECLARATION', )); foreach ($defs as $def) { - $body = $def->getChildByIndex(5); + $body = $def->getChildByIndex(6); if ($body->getTypeName() === 'n_EMPTY') { // Abstract method declaration. continue; } $exclude = array(); // Exclude uses of variables, unsets, and foreach loops // within closures - they are checked on their own $func_defs = $body->selectDescendantsOfType('n_FUNCTION_DECLARATION'); foreach ($func_defs as $func_def) { $vars = $func_def->selectDescendantsOfType('n_VARIABLE'); foreach ($vars as $var) { $exclude[$var->getID()] = true; } $unset_lists = $func_def->selectDescendantsOfType('n_UNSET_LIST'); foreach ($unset_lists as $unset_list) { $exclude[$unset_list->getID()] = true; } $foreaches = $func_def->selectDescendantsOfType('n_FOREACH'); foreach ($foreaches as $foreach) { $exclude[$foreach->getID()] = true; } } // Find all variables that are unset within the scope $unset_vars = array(); $unset_lists = $body->selectDescendantsOfType('n_UNSET_LIST'); foreach ($unset_lists as $unset_list) { if (isset($exclude[$unset_list->getID()])) { continue; } $unset_list_vars = $unset_list->selectDescendantsOfType('n_VARIABLE'); foreach ($unset_list_vars as $var) { $concrete = $this->getConcreteVariableString($var); $unset_vars[$concrete][] = $var->getOffset(); $exclude[$var->getID()] = true; } } // Find all reference variables in foreach expressions $reference_vars = array(); $foreaches = $body->selectDescendantsOfType('n_FOREACH'); foreach ($foreaches as $foreach) { if (isset($exclude[$foreach->getID()])) { continue; } $foreach_expr = $foreach->getChildOfType(0, 'n_FOREACH_EXPRESSION'); $var = $foreach_expr->getChildByIndex(2); if ($var->getTypeName() !== 'n_VARIABLE_REFERENCE') { continue; } $reference = $var->getChildByIndex(0); if ($reference->getTypeName() !== 'n_VARIABLE') { continue; } $reference_name = $this->getConcreteVariableString($reference); $reference_vars[$reference_name][] = $reference->getOffset(); $exclude[$reference->getID()] = true; // Exclude uses of the reference variable within the foreach loop $foreach_vars = $foreach->selectDescendantsOfType('n_VARIABLE'); foreach ($foreach_vars as $var) { $name = $this->getConcreteVariableString($var); if ($name === $reference_name) { $exclude[$var->getID()] = true; } } } // Allow usage if the reference variable is assigned to another // reference variable $binary = $body->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($binary as $expr) { if ($expr->getChildByIndex(1)->getConcreteString() !== '=') { continue; } $lval = $expr->getChildByIndex(0); if ($lval->getTypeName() !== 'n_VARIABLE') { continue; } $rval = $expr->getChildByIndex(2); if ($rval->getTypeName() !== 'n_VARIABLE_REFERENCE') { continue; } // Counts as unsetting a variable $concrete = $this->getConcreteVariableString($lval); $unset_vars[$concrete][] = $lval->getOffset(); $exclude[$lval->getID()] = true; } $all_vars = array(); $all = $body->selectDescendantsOfType('n_VARIABLE'); foreach ($all as $var) { if (isset($exclude[$var->getID()])) { continue; } $name = $this->getConcreteVariableString($var); if (!isset($reference_vars[$name])) { continue; } // Find the closest reference offset to this variable $reference_offset = null; foreach ($reference_vars[$name] as $offset) { if ($offset < $var->getOffset()) { $reference_offset = $offset; } else { break; } } if (!$reference_offset) { continue; } // Check if an unset exists between reference and usage of this // variable $warn = true; if (isset($unset_vars[$name])) { foreach ($unset_vars[$name] as $unset_offset) { if ($unset_offset > $reference_offset && $unset_offset < $var->getOffset()) { $warn = false; break; } } } if ($warn) { $this->raiseLintAtNode( $var, pht( 'This variable was used already as a by-reference iterator '. 'variable. Such variables survive outside the `%s` loop, '. 'do not reuse.', 'foreach')); } } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistStaticThisXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistStaticThisXHPASTLinterRule.php index 695d0858..e18c5135 100644 --- a/src/lint/linter/xhpast/rules/ArcanistStaticThisXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistStaticThisXHPASTLinterRule.php @@ -1,60 +1,60 @@ selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $attributes = $method ->getChildByIndex(0, 'n_METHOD_MODIFIER_LIST') ->selectDescendantsOfType('n_STRING'); $method_is_static = false; $method_is_abstract = false; foreach ($attributes as $attribute) { if (strtolower($attribute->getConcreteString()) === 'static') { $method_is_static = true; } if (strtolower($attribute->getConcreteString()) === 'abstract') { $method_is_abstract = true; } } if ($method_is_abstract) { continue; } if (!$method_is_static) { continue; } - $body = $method->getChildOfType(5, 'n_STATEMENT_LIST'); + $body = $method->getChildOfType(6, 'n_STATEMENT_LIST'); $variables = $body->selectDescendantsOfType('n_VARIABLE'); foreach ($variables as $variable) { if ($method_is_static && strtolower($variable->getConcreteString()) === '$this') { $this->raiseLintAtNode( $variable, pht( 'You can not reference `%s` inside a static method.', '$this')); } } } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistToStringExceptionXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistToStringExceptionXHPASTLinterRule.php index ab0eba56..315331ee 100644 --- a/src/lint/linter/xhpast/rules/ArcanistToStringExceptionXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistToStringExceptionXHPASTLinterRule.php @@ -1,43 +1,43 @@ selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $name = $method ->getChildOfType(2, 'n_STRING') ->getConcreteString(); if ($name != '__toString') { continue; } - $statements = $method->getChildByIndex(5); + $statements = $method->getChildByIndex(6); if ($statements->getTypeName() != 'n_STATEMENT_LIST') { continue; } $throws = $statements->selectDescendantsOfType('n_THROW'); foreach ($throws as $throw) { $this->raiseLintAtNode( $throw, pht( 'It is not possible to throw an `%s` from within the `%s` method.', 'Exception', '__toString')); } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php index 2a389e34..b1f997e9 100644 --- a/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistUndeclaredVariableXHPASTLinterRule.php @@ -1,373 +1,373 @@ selectDescendantsOfTypes(array( 'n_FUNCTION_DECLARATION', 'n_METHOD_DECLARATION', )); foreach ($defs as $def) { // We keep track of the first offset where scope becomes unknowable, and // silence any warnings after that. Default it to INT_MAX so we can min() // it later to keep track of the first problem we encounter. $scope_destroyed_at = PHP_INT_MAX; $declarations = array( '$this' => 0, ) + 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 // above. Put the tokens into the $vars array. $param_list = $def->getChildOfType(3, 'n_DECLARATION_PARAMETER_LIST'); $param_vars = $param_list->selectDescendantsOfType('n_VARIABLE'); foreach ($param_vars as $var) { $vars[] = $var; } // This is PHP5.3 closure syntax: function () use ($x) {}; $lexical_vars = $def ->getChildByIndex(4) ->selectDescendantsOfType('n_VARIABLE'); foreach ($lexical_vars as $var) { $vars[] = $var; } - $body = $def->getChildByIndex(5); + $body = $def->getChildByIndex(6); if ($body->getTypeName() === 'n_EMPTY') { // Abstract method declaration. continue; } $static_vars = $body ->selectDescendantsOfType('n_STATIC_DECLARATION') ->selectDescendantsOfType('n_VARIABLE'); foreach ($static_vars as $var) { $vars[] = $var; } $global_vars = $body ->selectDescendantsOfType('n_GLOBAL_DECLARATION_LIST'); foreach ($global_vars as $var_list) { foreach ($var_list->getChildren() as $var) { if ($var->getTypeName() === 'n_VARIABLE') { $vars[] = $var; } else { // Dynamic global variable, i.e. "global $$x;". $scope_destroyed_at = min($scope_destroyed_at, $var->getOffset()); // An error is raised elsewhere, no need to raise here. } } } // Include "catch (Exception $ex)", but not variables in the body of the // catch block. $catches = $body->selectDescendantsOfType('n_CATCH'); foreach ($catches as $catch) { $vars[] = $catch->getChildOfType(1, 'n_VARIABLE'); } $binary = $body->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($binary as $expr) { if ($expr->getChildByIndex(1)->getConcreteString() !== '=') { continue; } $lval = $expr->getChildByIndex(0); if ($lval->getTypeName() === 'n_VARIABLE') { $vars[] = $lval; } else if ($lval->getTypeName() === 'n_LIST') { // Recursively grab everything out of list(), since the grammar // permits list() to be nested. Also note that list() is ONLY valid // as an lval assignments, so we could safely lift this out of the // n_BINARY_EXPRESSION branch. $assign_vars = $lval->selectDescendantsOfType('n_VARIABLE'); foreach ($assign_vars as $var) { $vars[] = $var; } } if ($lval->getTypeName() === 'n_VARIABLE_VARIABLE') { $scope_destroyed_at = min($scope_destroyed_at, $lval->getOffset()); // No need to raise here since we raise an error elsewhere. } } $calls = $body->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($calls as $call) { $name = strtolower($call->getChildByIndex(0)->getConcreteString()); if ($name === 'empty' || $name === 'isset') { $params = $call ->getChildOfType(1, 'n_CALL_PARAMETER_LIST') ->selectDescendantsOfType('n_VARIABLE'); foreach ($params as $var) { $exclude_tokens[$var->getID()] = true; } continue; } if ($name !== 'extract') { continue; } $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; } 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 // two maps, one which just keeps track of which tokens are part of // declarations ($declaration_tokens) and one which has the first offset // where a variable is declared ($declarations). foreach ($vars as $var) { $concrete = $this->getConcreteVariableString($var); $declarations[$concrete] = min( idx($declarations, $concrete, PHP_INT_MAX), $var->getOffset()); $declaration_tokens[$var->getID()] = true; } // Excluded tokens are ones we don't "count" as being used, described // above. Put them into $exclude_tokens. $class_statics = $body ->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); $class_static_vars = $class_statics ->selectDescendantsOfType('n_VARIABLE'); foreach ($class_static_vars as $var) { $exclude_tokens[$var->getID()] = true; } // Find all the variables in scope, and figure out where they are used. // We want to find foreach() iterators which are both declared before and // used after the foreach() loop. $uses = array(); $all_vars = $body->selectDescendantsOfType('n_VARIABLE'); $all = array(); // NOTE: $all_vars is not a real array so we can't unset() it. foreach ($all_vars as $var) { // Be strict since it's easier; we don't let you reuse an iterator you // declared before a loop after the loop, even if you're just assigning // to it. $concrete = $this->getConcreteVariableString($var); $uses[$concrete][$var->getID()] = $var->getOffset(); if (isset($declaration_tokens[$var->getID()])) { // We know this is part of a declaration, so it's fine. continue; } if (isset($exclude_tokens[$var->getID()])) { // We know this is part of isset() or similar, so it's fine. continue; } $all[$var->getOffset()] = $concrete; } // Do foreach() last, we want to handle implicit redeclaration of a // variable already in scope since this probably means we're ovewriting a // local. // NOTE: Processing foreach expressions in order allows programs which // reuse iterator variables in other foreach() loops -- this is fine. We // have a separate warning to prevent nested loops from reusing the same // iterators. $foreaches = $body->selectDescendantsOfType('n_FOREACH'); $all_foreach_vars = array(); foreach ($foreaches as $foreach) { $foreach_expr = $foreach->getChildOfType(0, 'n_FOREACH_EXPRESSION'); $foreach_vars = array(); // Determine the end of the foreach() loop. $foreach_tokens = $foreach->getTokens(); $last_token = end($foreach_tokens); $foreach_end = $last_token->getOffset(); $key_var = $foreach_expr->getChildByIndex(1); if ($key_var->getTypeName() === 'n_VARIABLE') { $foreach_vars[] = $key_var; } $value_var = $foreach_expr->getChildByIndex(2); if ($value_var->getTypeName() === 'n_VARIABLE') { $foreach_vars[] = $value_var; } else { // The root-level token may be a reference, as in: // foreach ($a as $b => &$c) { ... } // Reach into the n_VARIABLE_REFERENCE node to grab the n_VARIABLE // node. $var = $value_var->getChildByIndex(0); if ($var->getTypeName() === 'n_VARIABLE_VARIABLE') { $var = $var->getChildByIndex(0); } $foreach_vars[] = $var; } // Remove all uses of the iterators inside of the foreach() loop from // the $uses map. foreach ($foreach_vars as $var) { $concrete = $this->getConcreteVariableString($var); $offset = $var->getOffset(); foreach ($uses[$concrete] as $id => $use_offset) { if (($use_offset >= $offset) && ($use_offset < $foreach_end)) { unset($uses[$concrete][$id]); } } $all_foreach_vars[] = $var; } } foreach ($all_foreach_vars as $var) { $concrete = $this->getConcreteVariableString($var); $offset = $var->getOffset(); // This is a declaration, exclude it from the "declare variables prior // to use" check below. unset($all[$var->getOffset()]); $vars[] = $var; } // Now rebuild declarations to include foreach(). foreach ($vars as $var) { $concrete = $this->getConcreteVariableString($var); $declarations[$concrete] = min( idx($declarations, $concrete, PHP_INT_MAX), $var->getOffset()); $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) { if (isset($exclude_strings[$string->getID()][$var])) { continue; } $all[$string->getOffset() + $offset - 1] = '$'.$var; } } } // Issue a warning for every variable token, unless it appears in a // declaration, we know about a prior declaration, we have explicitly // excluded it, or scope has been made unknowable before it appears. $issued_warnings = array(); foreach ($all as $offset => $concrete) { if ($offset >= $scope_destroyed_at) { // This appears after an extract() or $$var so we have no idea // whether it's legitimate or not. We raised a harshly-worded warning // when scope was made unknowable, so just ignore anything we can't // figure out. continue; } if ($offset >= idx($declarations, $concrete, PHP_INT_MAX)) { // The use appears after the variable is declared, so it's fine. continue; } if (!empty($issued_warnings[$concrete])) { // We've already issued a warning for this variable so we don't need // to issue another one. continue; } $this->raiseLintAtOffset( $offset, pht( 'Declare variables prior to use (even if you are passing them '. 'as reference parameters). You may have misspelled this '. 'variable name.'), $concrete); $issued_warnings[$concrete] = true; } } } } diff --git a/src/lint/linter/xhpast/rules/ArcanistUselessOverridingMethodXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistUselessOverridingMethodXHPASTLinterRule.php index 475ed25f..add72ca8 100644 --- a/src/lint/linter/xhpast/rules/ArcanistUselessOverridingMethodXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistUselessOverridingMethodXHPASTLinterRule.php @@ -1,102 +1,102 @@ selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $method_name = $method ->getChildOfType(2, 'n_STRING') ->getConcreteString(); $parameter_list = $method ->getChildOfType(3, 'n_DECLARATION_PARAMETER_LIST'); $parameters = array(); foreach ($parameter_list->getChildren() as $parameter) { $default = $parameter->getChildByIndex(2); $parameter = $parameter->getChildByIndex(1); if ($parameter->getTypeName() == 'n_VARIABLE_REFERENCE') { $parameter = $parameter->getChildOfType(0, 'n_VARIABLE'); } if ($default->getTypeName() != 'n_EMPTY') { continue 2; } $parameters[] = $parameter->getConcreteString(); } - $statements = $method->getChildByIndex(5); + $statements = $method->getChildByIndex(6); if ($statements->getTypeName() != 'n_STATEMENT_LIST') { continue; } if (count($statements->getChildren()) != 1) { continue; } $statement = $statements ->getChildOfType(0, 'n_STATEMENT') ->getChildByIndex(0); if ($statement->getTypeName() == 'n_RETURN') { $statement = $statement->getChildByIndex(0); } if ($statement->getTypeName() != 'n_FUNCTION_CALL') { continue; } $function = $statement->getChildByIndex(0); if ($function->getTypeName() != 'n_CLASS_STATIC_ACCESS') { continue; } $called_class = $function->getChildOfType(0, 'n_CLASS_NAME'); $called_method = $function->getChildOfType(1, 'n_STRING'); if ($called_class->getConcreteString() != 'parent') { continue; } else if ($called_method->getConcreteString() != $method_name) { continue; } $params = $statement ->getChildOfType(1, 'n_CALL_PARAMETER_LIST') ->getChildren(); foreach ($params as $param) { if ($param->getTypeName() != 'n_VARIABLE') { continue 2; } $expected = array_shift($parameters); if ($param->getConcreteString() != $expected) { continue 2; } } $this->raiseLintAtNode( $method, pht('Useless overriding method.')); } } }