diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -90,6 +90,7 @@ 'ArcanistDuplicateSwitchCaseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDuplicateSwitchCaseXHPASTLinterRule.php', 'ArcanistDynamicDefineXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDynamicDefineXHPASTLinterRule.php', 'ArcanistElseIfUsageXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistElseIfUsageXHPASTLinterRule.php', + 'ArcanistEmptyFileXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistEmptyFileXHPASTLinterRule.php', 'ArcanistEmptyStatementXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistEmptyStatementXHPASTLinterRule.php', 'ArcanistEventType' => 'events/constant/ArcanistEventType.php', 'ArcanistExitExpressionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistExitExpressionXHPASTLinterRule.php', @@ -375,6 +376,7 @@ 'ArcanistDuplicateSwitchCaseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistDynamicDefineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistElseIfUsageXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', + 'ArcanistEmptyFileXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistEmptyStatementXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistEventType' => 'PhutilEventType', 'ArcanistExitExpressionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', diff --git a/src/lint/linter/ArcanistTextLinter.php b/src/lint/linter/ArcanistTextLinter.php --- a/src/lint/linter/ArcanistTextLinter.php +++ b/src/lint/linter/ArcanistTextLinter.php @@ -13,6 +13,7 @@ const LINT_TRAILING_WHITESPACE = 6; const LINT_BOF_WHITESPACE = 8; const LINT_EOF_WHITESPACE = 9; + const LINT_EMPTY_FILE = 10; private $maxLineLength = 80; @@ -85,16 +86,23 @@ self::LINT_TRAILING_WHITESPACE => pht('Trailing Whitespace'), self::LINT_BOF_WHITESPACE => pht('Leading Whitespace at BOF'), self::LINT_EOF_WHITESPACE => pht('Trailing Whitespace at EOF'), + self::LINT_EMPTY_FILE => pht('Empty File'), ); } public function lintPath($path) { + $this->lintEmptyFile($path); + if (!strlen($this->getData($path))) { // If the file is empty, don't bother; particularly, don't require // the user to add a newline. return; } + if ($this->didStopAllLinters()) { + return; + } + $this->lintNewlines($path); $this->lintTabs($path); @@ -116,6 +124,29 @@ $this->lintEOFWhitespace($path); } + protected function lintEmptyFile($path) { + $data = $this->getData($path); + + // It is reasonable for certain file types to be completely empty, + // so they are excluded here. + switch ($filename = basename($this->getActivePath())) { + case '__init__.py': + return; + + default: + if (strlen($filename) && $filename[0] == '.') { + return; + } + } + + if (preg_match('/^\s*$/', $data)) { + $this->raiseLintAtPath( + self::LINT_EMPTY_FILE, + pht("Empty files usually don't serve any useful purpose.")); + $this->stopAllLinters(); + } + } + protected function lintNewlines($path) { $data = $this->getData($path); $pos = strpos($this->getData($path), "\r"); diff --git a/src/lint/linter/__tests__/text/empty-file.lint-test b/src/lint/linter/__tests__/text/empty.lint-test rename from src/lint/linter/__tests__/text/empty-file.lint-test rename to src/lint/linter/__tests__/text/empty.lint-test --- a/src/lint/linter/__tests__/text/empty-file.lint-test +++ b/src/lint/linter/__tests__/text/empty.lint-test @@ -1 +1,4 @@ + + ~~~~~~~~~~ +error:: diff --git a/src/lint/linter/__tests__/xhpast/empty.lint-test b/src/lint/linter/__tests__/xhpast/empty.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/xhpast/empty.lint-test @@ -0,0 +1,3 @@ + +~~~~~~~~~~ +warning:: diff --git a/src/lint/linter/__tests__/xhpast/not-empty.lint-test b/src/lint/linter/__tests__/xhpast/not-empty.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/xhpast/not-empty.lint-test @@ -0,0 +1,4 @@ +linter->raiseLintAtPath($this->getLintID(), $desc); + } + final protected function raiseLintAtToken( XHPASTToken $token, $desc, diff --git a/src/lint/linter/xhpast/rules/ArcanistEmptyFileXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistEmptyFileXHPASTLinterRule.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/ArcanistEmptyFileXHPASTLinterRule.php @@ -0,0 +1,35 @@ +getTokens(); + + foreach ($tokens as $token) { + switch ($token->getTypeName()) { + case 'T_OPEN_TAG': + case 'T_CLOSE_TAG': + case 'T_WHITESPACE': + break; + + default: + return; + } + } + + $this->raiseLintAtPath( + pht("Empty files usually don't serve any useful purpose.")); + } + +}