Page MenuHomePhorge

No OneTemporary

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 @@
<?php
final class ArcanistAbstractMethodBodyXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 108;
public function getLintName() {
return pht('`%s` Method Cannot Contain Body', 'abstract');
}
public function process(XHPASTNode $root) {
$methods = $root->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 @@
<?php
final class ArcanistInterfaceMethodBodyXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 114;
public function getLintName() {
return pht('`%s` Method Cannot Contain Body', 'interface');
}
public function process(XHPASTNode $root) {
$interfaces = $root->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 @@
<?php
final class ArcanistReusedAsIteratorXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 32;
public function getLintName() {
return pht('Variable Reused As Iterator');
}
public function process(XHPASTNode $root) {
$defs = $root->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 @@
<?php
/**
* Find cases where a `foreach` loop is being iterated using a variable
* reference and the same variable is used outside of the loop without calling
* `unset()` or reassigning the variable to another variable reference.
*
* COUNTEREXAMPLE
* foreach ($ar as &$a) {
* // ...
* }
* $a = 1; // <-- Raises an error for using $a
*/
final class ArcanistReusedIteratorReferenceXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 39;
public function getLintName() {
return pht('Reuse of Iterator References');
}
public function getLintSeverity() {
return ArcanistLintSeverity::SEVERITY_WARNING;
}
public function process(XHPASTNode $root) {
$defs = $root->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 @@
<?php
final class ArcanistStaticThisXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 13;
public function getLintName() {
return pht('Use of `%s` in Static Context', '$this');
}
public function process(XHPASTNode $root) {
$classes = $root->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 @@
<?php
final class ArcanistToStringExceptionXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 67;
public function getLintName() {
return pht('Throwing Exception in `%s` Method', '__toString');
}
public function process(XHPASTNode $root) {
$methods = $root->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 @@
<?php
final class ArcanistUndeclaredVariableXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 5;
public function getLintName() {
return pht('Use of Undeclared Variable');
}
public function process(XHPASTNode $root) {
// These things declare variables in a function:
// Explicit parameters
// Assignment
// Assignment via list()
// Static
// Global
// Lexical vars
// Builtins ($this)
// foreach()
// catch
//
// These things make lexical scope unknowable:
// Use of extract()
// Assignment to variable variables ($$x)
// Global with variable variables
//
// These things don't count as "using" a variable:
// isset()
// empty()
// Static class variables
//
// The general approach here is to find each function/method declaration,
// then:
//
// 1. Identify all the variable declarations, and where they first occur
// in the function/method declaration.
// 2. Identify all the uses that don't really count (as above).
// 3. Everything else must be a use of a variable.
// 4. For each variable, check if any uses occur before the declaration
// and warn about them.
//
// We also keep track of where lexical scope becomes unknowable (e.g.,
// because the function calls extract() or uses dynamic variables,
// preventing us from keeping track of which variables are defined) so we
// can stop issuing warnings after that.
//
// TODO: Support functions defined inside other functions which is commonly
// used with anonymous functions.
//
// TODO: parse_str() also makes lexical scope unknowable, see D13857.
$defs = $root->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 @@
<?php
final class ArcanistUselessOverridingMethodXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 63;
public function getLintName() {
return pht('Useless Overriding Method');
}
public function getLintSeverity() {
return ArcanistLintSeverity::SEVERITY_ADVICE;
}
public function process(XHPASTNode $root) {
$methods = $root->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.'));
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 19, 17:02 (21 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014876
Default Alt Text
(39 KB)

Event Timeline