public function process(XHPASTNode $root) { $all_paren_groups = $root->selectDescendantsOfTypes(array('n_ARRAY_VALUE_LIST', 'n_ASSIGNMENT_LIST', 'n_CALL_PARAMETER_LIST', 'n_DECLARATION_PARAMETER_LIST', 'n_CONTROL_CONDITION', 'n_FOR_EXPRESSION', 'n_FOREACH_EXPRESSION')); foreach ($all_paren_groups as $group) { $tokens = $group->getTokens(); $token_o = array_shift($tokens); $token_c = array_pop($tokens); $nonsem_o = $token_o->getNonsemanticTokensAfter(); $nonsem_c = $token_c->getNonsemanticTokensBefore(); if (!$nonsem_o) { continue; } $raise = array(); $string_o = implode('', mpull($nonsem_o, 'getValue')); if (preg_match('/^[ ]+$/', $string_o)) { $raise[] = array($nonsem_o, $string_o); } if ($nonsem_o !== $nonsem_c) { $string_c = implode('', mpull($nonsem_c, 'getValue')); if (preg_match('/^[ ]+$/', $string_c)) { $raise[] = array($nonsem_c, $string_c); } } foreach ($raise as $warning) { list($tokens, $string) = $warning; $this->raiseLintAtOffset(reset($tokens)->getOffset(), pht('Parentheses should hug their contents.'), $string, ''); } } }
public function process(XHPASTNode $root) { $parser = new PhutilDocblockParser(); $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $is_final = false; $is_abstract = false; $is_concrete_extensible = false; $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES'); foreach ($attributes->getChildren() as $child) { if ($child->getConcreteString() == 'final') { $is_final = true; } if ($child->getConcreteString() == 'abstract') { $is_abstract = true; } } $docblock = $class->getDocblockToken(); if ($docblock) { list($text, $specials) = $parser->parse($docblock->getValue()); $is_concrete_extensible = idx($specials, 'concrete-extensible'); } if (!$is_final && !$is_abstract && !$is_concrete_extensible) { $this->raiseLintAtNode($class->getChildOfType(1, 'n_CLASS_NAME'), pht('This class is neither `%s` nor `%s`, and does not have ' . 'a docblock marking it `%s`.', 'final', 'abstract', '@concrete-extensible')); } } }
public function process(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); static $operators = array('-' => true, '/' => true, '-=' => true, '/=' => true, '<=' => true, '<' => true, '==' => true, '===' => true, '!=' => true, '!==' => true, '>=' => true, '>' => true); static $logical = array('||' => true, '&&' => true); foreach ($expressions as $expr) { $operator = $expr->getChildByIndex(1)->getConcreteString(); if (!empty($operators[$operator])) { $left = $expr->getChildByIndex(0)->getSemanticString(); $right = $expr->getChildByIndex(2)->getSemanticString(); if ($left === $right) { $this->raiseLintAtNode($expr, pht('Both sides of this expression are identical, so it always ' . 'evaluates to a constant.')); } } if (!empty($logical[$operator])) { $left = $expr->getChildByIndex(0)->getSemanticString(); $right = $expr->getChildByIndex(2)->getSemanticString(); // NOTE: These will be null to indicate "could not evaluate". $left = $this->evaluateStaticBoolean($left); $right = $this->evaluateStaticBoolean($right); if ($operator === '||' && ($left === true || $right === true) || $operator === '&&' && ($left === false || $right === false)) { $this->raiseLintAtNode($expr, pht('The logical value of this expression is static. ' . 'Did you forget to remove some debugging code?')); } } } }
public function process(XHPASTNode $root) { $nodes = $root->selectDescendantsOfType('n_GLOBAL_DECLARATION_LIST'); foreach ($nodes as $node) { $this->raiseLintAtNode($node, pht('Limit the use of global variables. Global variables are ' . 'generally a bad idea and should be avoided when possible.')); } }
private function lintStrposUsedForStart(XHPASTNode $root) { $expressions = $root->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($expressions as $expression) { $operator = $expression->getChildOfType(1, 'n_OPERATOR'); $operator = $operator->getConcreteString(); if ($operator !== '===' && $operator !== '!==') { continue; } $zero = $expression->getChildByIndex(0); if ($zero->getTypeName() === 'n_NUMERIC_SCALAR' && $zero->getConcreteString() === '0') { $strpos = $expression->getChildByIndex(2); } else { $strpos = $zero; $zero = $expression->getChildByIndex(2); if ($zero->getTypeName() !== 'n_NUMERIC_SCALAR' || $zero->getConcreteString() !== '0') { continue; } } if ($strpos->getTypeName() !== 'n_FUNCTION_CALL') { continue; } $name = strtolower($strpos->getChildByIndex(0)->getConcreteString()); if ($name === 'strpos') { $this->raiseLintAtNode($strpos, pht('Use %s for checking if the string starts with something.', 'strncmp()')); } else { if ($name === 'stripos') { $this->raiseLintAtNode($strpos, pht('Use %s for checking if the string starts with something.', 'strncasecmp()')); } } } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType('T_ELSEIF'); foreach ($tokens as $token) { $this->raiseLintAtToken($token, pht('Usage of `%s` is preferred over `%s`.', 'else if', 'elseif'), 'else if'); } }
public function process(XHPASTNode $root) { $keywords = $root->selectTokensOfTypes(array('T_REQUIRE_ONCE', 'T_REQUIRE', 'T_EVAL', 'T_INCLUDE_ONCE', 'T_INCLUDE', 'T_LOGICAL_OR', 'T_LOGICAL_XOR', 'T_LOGICAL_AND', 'T_PRINT', 'T_INSTANCEOF', 'T_CLONE', 'T_NEW', 'T_EXIT', 'T_IF', 'T_ELSEIF', 'T_ELSE', 'T_ENDIF', 'T_ECHO', 'T_DO', 'T_WHILE', 'T_ENDWHILE', 'T_FOR', 'T_ENDFOR', 'T_FOREACH', 'T_ENDFOREACH', 'T_DECLARE', 'T_ENDDECLARE', 'T_AS', 'T_SWITCH', 'T_ENDSWITCH', 'T_CASE', 'T_DEFAULT', 'T_BREAK', 'T_CONTINUE', 'T_GOTO', 'T_FUNCTION', 'T_CONST', 'T_RETURN', 'T_TRY', 'T_CATCH', 'T_THROW', 'T_USE', 'T_GLOBAL', 'T_PUBLIC', 'T_PROTECTED', 'T_PRIVATE', 'T_FINAL', 'T_ABSTRACT', 'T_STATIC', 'T_VAR', 'T_UNSET', 'T_ISSET', 'T_EMPTY', 'T_HALT_COMPILER', 'T_CLASS', 'T_INTERFACE', 'T_EXTENDS', 'T_IMPLEMENTS', 'T_LIST', 'T_ARRAY', 'T_NAMESPACE', 'T_INSTEADOF', 'T_CALLABLE', 'T_TRAIT', 'T_YIELD', 'T_FINALLY')); foreach ($keywords as $keyword) { $value = $keyword->getValue(); if ($value != strtolower($value)) { $this->raiseLintAtToken($keyword, pht('Convention: spell keyword `%s` as `%s`.', $value, strtolower($value)), strtolower($value)); } } $symbols = $root->selectDescendantsOfType('n_SYMBOL_NAME'); foreach ($symbols as $symbol) { static $interesting_symbols = array('false' => true, 'null' => true, 'true' => true); $symbol_name = $symbol->getConcreteString(); if ($symbol->getParentNode()->getTypeName() == 'n_FUNCTION_CALL') { continue; } if (idx($interesting_symbols, strtolower($symbol_name))) { if ($symbol_name != strtolower($symbol_name)) { $this->raiseLintAtNode($symbol, pht('Convention: spell keyword `%s` as `%s`.', $symbol_name, strtolower($symbol_name)), strtolower($symbol_name)); } } } $magic_constants = $root->selectTokensOfTypes(array('T_CLASS_C', 'T_METHOD_C', 'T_FUNC_C', 'T_LINE', 'T_FILE', 'T_NS_C', 'T_DIR', 'T_TRAIT_C')); foreach ($magic_constants as $magic_constant) { $value = $magic_constant->getValue(); if ($value != strtoupper($value)) { $this->raiseLintAtToken($magic_constant, pht('Magic constants should be uppercase.'), strtoupper($value)); } } }
public function process(XHPASTNode $root) { static $functions = array('fprintf' => 1, 'printf' => 0, 'sprintf' => 0, 'vfprintf' => 1, 'csprintf' => 0, 'execx' => 0, 'exec_manual' => 0, 'hgsprintf' => 0, 'hsprintf' => 0, 'jsprintf' => 0, 'pht' => 0, 'phutil_passthru' => 0, 'qsprintf' => 1, 'queryfx' => 1, 'queryfx_all' => 1, 'queryfx_one' => 1, 'vcsprintf' => 0, 'vqsprintf' => 1); $function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($function_calls as $call) { $name = $call->getChildByIndex(0)->getConcreteString(); $name = strtolower($name); $start = idx($functions + $this->printfFunctions, $name); if ($start === null) { continue; } $parameters = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST'); $argc = count($parameters->getChildren()) - $start; if ($argc < 1) { $this->raiseLintAtNode($call, pht('This function is expected to have a format string.')); continue; } $format = $parameters->getChildByIndex($start); if ($format->getTypeName() != 'n_STRING_SCALAR') { continue; } $argv = array($format->evalStatic()) + array_fill(0, $argc, null); try { xsprintf(null, null, $argv); } catch (BadFunctionCallException $ex) { $this->raiseLintAtNode($call, str_replace('xsprintf', $name, $ex->getMessage())); } catch (InvalidArgumentException $ex) { // Ignore. } } }
public function process(XHPASTNode $root) { $calls = $root->selectDescendantsOfTypes(array('n_FUNCTION_CALL', 'n_METHOD_CALL')); foreach ($calls as $call) { $params = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST'); $tokens = $params->getTokens(); $first = head($tokens); $leading = $first->getNonsemanticTokensBefore(); $leading_text = implode('', mpull($leading, 'getValue')); if (preg_match('/^\\s+$/', $leading_text)) { $this->raiseLintAtOffset($first->getOffset() - strlen($leading_text), pht('Convention: no spaces before opening parenthesis in calls.'), $leading_text, ''); } } foreach ($calls as $call) { // If the last parameter of a call is a HEREDOC, don't apply this rule. $params = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST')->getChildren(); if ($params) { $last_param = last($params); if ($last_param->getTypeName() === 'n_HEREDOC') { continue; } } $tokens = $call->getTokens(); $last = array_pop($tokens); $trailing = $last->getNonsemanticTokensBefore(); $trailing_text = implode('', mpull($trailing, 'getValue')); if (preg_match('/^\\s+$/', $trailing_text)) { $this->raiseLintAtOffset($last->getOffset() - strlen($trailing_text), pht('Convention: no spaces before closing parenthesis in calls.'), $trailing_text, ''); } } }
public function process(XHPASTNode $root) { $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES'); $is_final = false; foreach ($attributes->getChildren() as $attribute) { if ($attribute->getConcreteString() == 'final') { $is_final = true; break; } } if (!$is_final) { continue; } $methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $attributes = $method->getChildOfType(0, 'n_METHOD_MODIFIER_LIST'); foreach ($attributes->getChildren() as $attribute) { if ($attribute->getConcreteString() == 'final') { $this->raiseLintAtNode($attribute, pht('Unnecessary %s modifier in %s class.', 'final', 'final')); } } } } }
public function process(XHPASTNode $root) { $arrays = $root->selectDescendantsOfType('n_ARRAY_LITERAL'); foreach ($arrays as $array) { $array_values = $array->getChildOfType(0, 'n_ARRAY_VALUE_LIST')->getChildrenOfType('n_ARRAY_VALUE'); if (!$array_values) { // There is no need to check an empty array. continue; } $multiline = $array->getLineNumber() != $array->getEndLineNumber(); if (!$multiline) { continue; } foreach ($array_values as $value) { list($before, $after) = $value->getSurroundingNonsemanticTokens(); if (strpos(implode('', mpull($before, 'getValue')), "\n") === false) { if (last($before) && last($before)->isAnyWhitespace()) { $token = last($before); $replacement = "\n" . $value->getIndentation(); } else { $token = head($value->getTokens()); $replacement = "\n" . $value->getIndentation() . $token->getValue(); } $this->raiseLintAtToken($token, pht('Array elements should each occupy a single line.'), $replacement); } } } }
public function process(XHPASTNode $root) { $index_accesses = $root->selectDescendantsOfType('n_INDEX_ACCESS'); foreach ($index_accesses as $index_access) { $tokens = $index_access->getChildByIndex(1)->getTokens(); if (!$tokens) { continue; } $left_brace = head($tokens)->getPrevToken(); while (!$left_brace->isSemantic()) { $left_brace = $left_brace->getPrevToken(); } $right_brace = last($tokens)->getNextToken(); while (!$right_brace->isSemantic()) { $right_brace = $right_brace->getNextToken(); } if ($left_brace->getValue() == '{' || $right_brace->getValue() == '}') { $replacement = null; foreach ($index_access->getTokens() as $token) { if ($token === $left_brace) { $replacement .= '['; } else { if ($token === $right_brace) { $replacement .= ']'; } else { $replacement .= $token->getValue(); } } } $this->raiseLintAtNode($index_access, pht('Use `%s` instead of `%s`.', "\$x['key']", "\$x{'key'}"), $replacement); } } }
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'); $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')); } } } } }
public function process(XHPASTNode $root) { $vvars = $root->selectDescendantsOfType('n_VARIABLE_VARIABLE'); foreach ($vvars as $vvar) { $this->raiseLintAtNode($vvar, pht('Rewrite this code to use an array. Variable variables are unclear ' . 'and hinder static analysis.')); } }
public function process(XHPASTNode $root) { $class_declarations = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($class_declarations as $class_declaration) { $class_name = $class_declaration->getChildOfType(1, 'n_CLASS_NAME')->getConcreteString(); $class_static_accesses = $class_declaration->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); $closures = $this->getAnonymousClosures($class_declaration); foreach ($class_static_accesses as $class_static_access) { $class_ref = $class_static_access->getChildByIndex(0); if ($class_ref->getTypeName() != 'n_CLASS_NAME') { continue; } $class_ref_name = $class_ref->getConcreteString(); if (strtolower($class_name) == strtolower($class_ref_name)) { $in_closure = false; foreach ($closures as $closure) { if ($class_ref->isDescendantOf($closure)) { $in_closure = true; break; } } if (version_compare($this->version, '5.4.0', '>=') || !$in_closure) { $this->raiseLintAtNode($class_ref, pht('Use `%s` for local static member references.', 'self::'), 'self'); } } } } }
public function process(XHPASTNode $root) { foreach ($root->selectDescendantsOfType('n_STATEMENT_LIST') as $list) { $tokens = $list->getTokens(); if (!$tokens || head($tokens)->getValue() != '{') { continue; } list($before, $after) = $list->getSurroundingNonsemanticTokens(); if (!$before) { $first = head($tokens); // Only insert the space if we're after a closing parenthesis. If // we're in a construct like "else{}", other rules will insert space // after the 'else' correctly. $prev = $first->getPrevToken(); if (!$prev || $prev->getValue() !== ')') { continue; } $this->raiseLintAtToken($first, pht('Put opening braces on the same line as control statements and ' . 'declarations, with a single space before them.'), ' ' . $first->getValue()); } else { if (count($before) === 1) { $before = reset($before); if ($before->getValue() !== ' ') { $this->raiseLintAtToken($before, pht('Put opening braces on the same line as control statements and ' . 'declarations, with a single space before them.'), ' '); } } } } $nodes = $root->selectDescendantsOfType('n_STATEMENT'); foreach ($nodes as $node) { $parent = $node->getParentNode(); if (!$parent) { continue; } $type = $parent->getTypeName(); if ($type != 'n_STATEMENT_LIST' && $type != 'n_DECLARE') { $this->raiseLintAtNode($node, pht('Use braces to surround a statement block.')); } } $nodes = $root->selectDescendantsOfTypes(array('n_DO_WHILE', 'n_ELSE', 'n_ELSEIF')); foreach ($nodes as $list) { $tokens = $list->getTokens(); if (!$tokens || last($tokens)->getValue() != '}') { continue; } list($before, $after) = $list->getSurroundingNonsemanticTokens(); if (!$before) { $first = last($tokens); $this->raiseLintAtToken($first, pht('Put opening braces on the same line as control statements and ' . 'declarations, with a single space before them.'), ' ' . $first->getValue()); } else { if (count($before) === 1) { $before = reset($before); if ($before->getValue() !== ' ') { $this->raiseLintAtToken($before, pht('Put opening braces on the same line as control statements and ' . 'declarations, with a single space before them.'), ' '); } } } } }
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(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.')); } }
private function isInAnonymousFunction(XHPASTNode $node) { while ($node) { if ($node->getTypeName() == 'n_FUNCTION_DECLARATION' && $node->getChildByIndex(2)->getTypeName() == 'n_EMPTY') { return true; } $node = $node->getParentNode(); } }
public function process(XHPASTNode $root) { $tokens = $root->getTokens(); foreach ($tokens as $token) { if ($token->getTypeName() === 'T_OPEN_TAG_WITH_ECHO') { $this->raiseLintAtToken($token, pht('Avoid the PHP echo short form, "%s".', '<?=')); } } }
public function process(XHPASTNode $root) { $nodes = $root->selectDescendantsOfType('n_CALL_PARAMETER_LIST'); foreach ($nodes as $node) { $parameters = $node->getChildrenOfType('n_VARIABLE_REFERENCE'); foreach ($parameters as $parameter) { $this->raiseLintAtNode($parameter, pht('Call-time pass-by-reference calls are prohibited.')); } } }
public function process(XHPASTNode $root) { $tokens = $root->selectTokensOfType(';'); foreach ($tokens as $token) { $prev = $token->getPrevToken(); if ($prev->isAnyWhitespace()) { $this->raiseLintAtToken($prev, pht('Space found before semicolon.'), ''); } } }
public function process(XHPASTNode $root) { foreach ($root->selectTokensOfType('T_COMMENT') as $comment) { $value = $comment->getValue(); if ($value[0] !== '#') { continue; } $this->raiseLintAtOffset($comment->getOffset(), pht('Use `%s` single-line comments, not `%s`.', '//', '#'), '#', preg_match('/^#\\S/', $value) ? '// ' : '//'); } }
public function process(XHPASTNode $root) { $members = $root->selectDescendantsOfType('n_CLASS_MEMBER_DECLARATION_LIST'); foreach ($members as $member) { $modifiers = $this->getModifiers($member); if (isset($modifiers['public'])) { $this->raiseLintAtNode($member, pht('`%s` properties should be avoided. Instead of exposing ' . 'the property value directly, consider using getter ' . 'and setter methods.', 'public')); } } }
public function process(XHPASTNode $root) { $inline_html = $root->selectDescendantsOfType('n_INLINE_HTML'); if ($inline_html) { return; } foreach ($root->selectTokensOfType('T_CLOSE_TAG') as $token) { $this->raiseLintAtToken($token, pht('Do not use the PHP closing tag, "%s".', '?>')); } }
public function process(XHPASTNode $root) { $namespaces = $root->selectDescendantsOfType('n_NAMESPACE'); foreach ($namespaces as $namespace) { $nested_namespaces = $namespace->selectDescendantsOfType('n_NAMESPACE'); foreach ($nested_namespaces as $nested_namespace) { $this->raiseLintAtNode($nested_namespace, pht('`%s` declarations cannot be nested. ' . 'This construct will cause a PHP fatal error.', 'namespace')); } } }
public function process(XHPASTNode $root) { $logical_ands = $root->selectTokensOfType('T_LOGICAL_AND'); $logical_ors = $root->selectTokensOfType('T_LOGICAL_OR'); foreach ($logical_ands as $logical_and) { $this->raiseLintAtToken($logical_and, pht('Use `%s` instead of `%s`.', '&&', 'and'), '&&'); } foreach ($logical_ors as $logical_or) { $this->raiseLintAtToken($logical_or, pht('Use `%s` instead of `%s`.', '||', 'or'), '||'); } }
public function process(XHPASTNode $root) { $used_vars = array(); $for_loops = $root->selectDescendantsOfType('n_FOR'); foreach ($for_loops as $for_loop) { $var_map = array(); // Find all the variables that are assigned to in the for() expression. $for_expr = $for_loop->getChildOfType(0, 'n_FOR_EXPRESSION'); $bin_exprs = $for_expr->selectDescendantsOfType('n_BINARY_EXPRESSION'); foreach ($bin_exprs as $bin_expr) { if ($bin_expr->getChildByIndex(1)->getConcreteString() === '=') { $var = $bin_expr->getChildByIndex(0); $var_map[$var->getConcreteString()] = $var; } } $used_vars[$for_loop->getID()] = $var_map; } $foreach_loops = $root->selectDescendantsOfType('n_FOREACH'); foreach ($foreach_loops as $foreach_loop) { $var_map = array(); $foreach_expr = $foreach_loop->getChildOfType(0, 'n_FOREACH_EXPRESSION'); // We might use one or two vars, i.e. "foreach ($x as $y => $z)" or // "foreach ($x as $y)". $possible_used_vars = array($foreach_expr->getChildByIndex(1), $foreach_expr->getChildByIndex(2)); foreach ($possible_used_vars as $var) { if ($var->getTypeName() === 'n_EMPTY') { continue; } $name = $var->getConcreteString(); $name = trim($name, '&'); // Get rid of ref silliness. $var_map[$name] = $var; } $used_vars[$foreach_loop->getID()] = $var_map; } $all_loops = $for_loops->add($foreach_loops); foreach ($all_loops as $loop) { $child_loops = $loop->selectDescendantsOfTypes(array('n_FOR', 'n_FOREACH')); $outer_vars = $used_vars[$loop->getID()]; foreach ($child_loops as $inner_loop) { $inner_vars = $used_vars[$inner_loop->getID()]; $shared = array_intersect_key($outer_vars, $inner_vars); if ($shared) { $shared_desc = implode(', ', array_keys($shared)); $message = $this->raiseLintAtNode($inner_loop->getChildByIndex(0), pht('This loop reuses iterator variables (%s) from an ' . 'outer loop. You might be clobbering the outer iterator. ' . 'Change the inner loop to use a different iterator name.', $shared_desc)); $locations = array(); foreach ($shared as $var) { $locations[] = $this->getOtherLocation($var->getOffset()); } $message->setOtherLocations($locations); } } } }
public function process(XHPASTNode $root) { $nodes = $root->selectDescendantsOfType('n_NEW'); foreach ($nodes as $node) { $class = $node->getChildByIndex(0); $params = $node->getChildByIndex(1); if ($params->getTypeName() == 'n_EMPTY') { $this->raiseLintAtNode($class, pht('Use parentheses when invoking a constructor.'), $class->getConcreteString() . '()'); } } }
public function process(XHPASTNode $root) { $methods = $root->selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $modifiers = $this->getModifiers($method); $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')); } } }
public function process(XHPASTNode $root) { $references = $root->selectDescendantsOfType('n_VARIABLE_REFERENCE'); foreach ($references as $reference) { $variable = $reference->getChildByIndex(0); list($before, $after) = $variable->getSurroundingNonsemanticTokens(); if ($before) { $this->raiseLintAtOffset(head($before)->getOffset(), pht('Variable references should not be prefixed with whitespace.'), implode('', mpull($before, 'getValue')), ''); } } }