From c5df885d7bd59a60abb0c000abb4bef452172e3c Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Tue, 7 Apr 2015 07:24:20 +1000 Subject: [PATCH] Add a linter rule to detect unnecessary semicolons Summary: Ref T7409. Adds a rule to detect unnecessary semicolons. The most common scenario I've seen in the wild is the use of semicolons after a class definition: ```lang=php class MyClass { // ... }; ``` Test Plan: Added unit tests. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T7409 Differential Revision: https://secure.phabricator.com/D12139 --- src/lint/linter/ArcanistXHPASTLinter.php | 24 ++++++++++++++++++- .../xhpast/empty-statement.lint-test | 11 +++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/lint/linter/__tests__/xhpast/empty-statement.lint-test diff --git a/src/lint/linter/ArcanistXHPASTLinter.php b/src/lint/linter/ArcanistXHPASTLinter.php index 20612d21..f5860014 100644 --- a/src/lint/linter/ArcanistXHPASTLinter.php +++ b/src/lint/linter/ArcanistXHPASTLinter.php @@ -55,6 +55,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { const LINT_CALL_TIME_PASS_BY_REF = 53; const LINT_FORMATTED_STRING = 54; const LINT_UNNECESSARY_FINAL_MODIFIER = 55; + const LINT_UNNECESSARY_SEMICOLON = 56; private $blacklistedFunctions = array(); private $naminghook; @@ -123,6 +124,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { self::LINT_CALL_TIME_PASS_BY_REF => 'Call-Time Pass-By-Reference', self::LINT_FORMATTED_STRING => 'Formatted String', self::LINT_UNNECESSARY_FINAL_MODIFIER => 'Unnecessary Final Modifier', + self::LINT_UNNECESSARY_SEMICOLON => 'Unnecessary Semicolon', ); } @@ -166,6 +168,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { self::LINT_CONSTRUCTOR_PARENTHESES => $advice, self::LINT_IMPLICIT_VISIBILITY => $advice, self::LINT_UNNECESSARY_FINAL_MODIFIER => $advice, + self::LINT_UNNECESSARY_SEMICOLON => $advice, ); } @@ -233,7 +236,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { public function getVersion() { // The version number should be incremented whenever a new rule is added. - return '17'; + return '18'; } protected function resolveFuture($path, Future $future) { @@ -312,6 +315,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { 'lintCallTimePassByReference' => self::LINT_CALL_TIME_PASS_BY_REF, 'lintFormattedString' => self::LINT_FORMATTED_STRING, 'lintUnnecessaryFinalModifier' => self::LINT_UNNECESSARY_FINAL_MODIFIER, + 'lintUnnecessarySemicolons' => self::LINT_UNNECESSARY_SEMICOLON, ); foreach ($method_codes as $method => $codes) { @@ -3229,6 +3233,24 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter { } } + private function lintUnnecessarySemicolons(XHPASTNode $root) { + $statements = $root->selectDescendantsOfType('n_STATEMENT'); + + foreach ($statements as $statement) { + if ($statement->getParentNode()->getTypeName() == 'n_DECLARE') { + continue; + } + + if ($statement->getSemanticString() == ';') { + $this->raiseLintAtNode( + $statement, + self::LINT_UNNECESSARY_SEMICOLON, + pht('Unnecessary semicolons after statement.'), + ''); + } + } + } + public function getSuperGlobalNames() { return array( '$GLOBALS', diff --git a/src/lint/linter/__tests__/xhpast/empty-statement.lint-test b/src/lint/linter/__tests__/xhpast/empty-statement.lint-test new file mode 100644 index 00000000..a9ccd179 --- /dev/null +++ b/src/lint/linter/__tests__/xhpast/empty-statement.lint-test @@ -0,0 +1,11 @@ +