diff --git a/src/lint/linter/ArcanistXHPASTLinter.php b/src/lint/linter/ArcanistXHPASTLinter.php --- a/src/lint/linter/ArcanistXHPASTLinter.php +++ b/src/lint/linter/ArcanistXHPASTLinter.php @@ -61,8 +61,10 @@ const LINT_INNER_FUNCTION = 59; const LINT_DEFAULT_PARAMETERS = 60; const LINT_LOWERCASE_FUNCTIONS = 61; + const LINT_INDENTATION = 62; private $blacklistedFunctions = array(); + private $indentation = 2; private $naminghook; private $printfFunctions = array(); private $switchhook; @@ -191,6 +193,8 @@ => pht('Default Parameters'), self::LINT_LOWERCASE_FUNCTIONS => pht('Lowercase Functions'), + self::LINT_INDENTATION + => pht('Indentation'), ); } @@ -240,6 +244,7 @@ self::LINT_INNER_FUNCTION => $warning, self::LINT_DEFAULT_PARAMETERS => $warning, self::LINT_LOWERCASE_FUNCTIONS => $advice, + self::LINT_INDENTATION => $advice, ); } @@ -249,6 +254,11 @@ 'type' => 'optional map', 'help' => pht('Blacklisted functions which should not be used.'), ), + 'xhpast.indentation' => array( + 'type' => 'optional int', + 'help' => pht( + 'Number of whitespace characters to be used for indentation.'), + ), 'xhpast.naminghook' => array( 'type' => 'optional string', 'help' => pht( @@ -285,6 +295,9 @@ case 'xhpast.blacklisted.function': $this->blacklistedFunctions = $value; return; + case 'xhpast.indentation': + $this->indentation = $value; + return; case 'xhpast.naminghook': $this->naminghook = $value; return; @@ -307,7 +320,7 @@ public function getVersion() { // The version number should be incremented whenever a new rule is added. - return '24'; + return '25'; } protected function resolveFuture($path, Future $future) { @@ -395,6 +408,7 @@ 'lintInnerFunctions' => self::LINT_INNER_FUNCTION, 'lintDefaultParameters' => self::LINT_DEFAULT_PARAMETERS, 'lintLowercaseFunctions' => self::LINT_LOWERCASE_FUNCTIONS, + 'lintIndentation' => self::LINT_INDENTATION, ); foreach ($method_codes as $method => $codes) { @@ -3727,6 +3741,42 @@ } } + private function lintIndentation(XHPASTNode $root) { + $nodes = $root->selectDescendantsOfType('n_STATEMENT'); + + foreach ($nodes as $node) { + $indent = $node->getIndentation(); + $expected = $this->getExpectedIndentation($node); + + if ($indent != $expected) { + $this->raiseLintAtNode( + $node, + self::LINT_INDENTATION, + pht('Incorrect indentation, expected "%s"', $expected)); + } + } + } + + private function getExpectedIndentation(XHPASTNode $node) { + $level = 0; + + for (; $node !== null; $node = $node->getParentNode()) { + switch ($node->getTypeName()) { + case 'n_STATEMENT_LIST': + $level++; + break; + + default: + break; + } + } + + if ($level <= 0) { + return ''; + } + return str_repeat(' ', $this->indentation * ($level - 1)); + } + /** * Retrieve all calls to some specified function(s). * diff --git a/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test b/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test --- a/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test +++ b/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test @@ -1,8 +1,8 @@ m()[0]; ~~~~~~~~~~ error:3:5 -error:4:9 ~~~~~~~~~~ ~~~~~~~~~~ {"config": {"xhpast.php-version": "5.3.0"}} diff --git a/src/lint/linter/__tests__/xhpast/switches.lint-test b/src/lint/linter/__tests__/xhpast/switches.lint-test --- a/src/lint/linter/__tests__/xhpast/switches.lint-test +++ b/src/lint/linter/__tests__/xhpast/switches.lint-test @@ -64,9 +64,17 @@ return; } case 4: - function f() { throw new Exception(); } - g(function() { return; }); - final class C { public function m() { return; } } + function f() { + throw new Exception(); + } + g(function() { + return; + }); + final class C { + public function m() { + return; + } + } interface I {} case 5: do { @@ -89,8 +97,8 @@ warning:53:3 warning:57:3 warning:66:3 -warning:71:3 -warning:75:3 +warning:79:3 +warning:83:3 ~~~~~~~~~~ ~~~~~~~~~~ {"config":{"xhpast.switchhook":"ArcanistTestXHPASTLintSwitchHook"}} diff --git a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test --- a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test +++ b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test @@ -184,6 +184,22 @@ error:51:10 worst ever warning:61:3 error:87:3 This stuff is basically testing the lexer/parser for function decls. +advice:90:29 +advice:90:35 +advice:91:30 +advice:91:36 +advice:92:36 +advice:92:42 +advice:93:37 +advice:93:43 +advice:94:27 +advice:94:33 +advice:95:28 +advice:95:34 +advice:96:34 +advice:96:40 +advice:97:35 +advice:97:41 error:104:15 Variables in instance derefs should be checked, static should not. error:118:3 isset() and empty() should not trigger errors. error:122:3 Should only warn once in this function.