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 @@ -154,6 +154,8 @@ 'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php', 'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php', 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', + 'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php', + 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestcase.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php', 'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php', @@ -335,6 +337,8 @@ 'ArcanistRepositoryAPIMiscTestCase' => 'ArcanistTestCase', 'ArcanistRepositoryAPIStateTestCase' => 'ArcanistTestCase', 'ArcanistRevertWorkflow' => 'ArcanistWorkflow', + 'ArcanistRuboCopLinter' => 'ArcanistExternalLinter', + 'ArcanistRuboCopLinterTestCase' => 'ArcanistArcanistLinterTestCase', 'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinterTestCase' => 'ArcanistArcanistLinterTestCase', 'ArcanistScriptAndRegexLinter' => 'ArcanistLinter', diff --git a/src/lint/linter/ArcanistRuboCopLinter.php b/src/lint/linter/ArcanistRuboCopLinter.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/ArcanistRuboCopLinter.php @@ -0,0 +1,132 @@ +getExecutableCommand()); + + $matches = array(); + if (preg_match('/^(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { + return $matches['version']; + } else { + return false; + } + } + + public function getInstallInstructions() { + return pht('Install RuboCop using `gem install rubocop`.'); + } + + public function shouldExpectCommandErrors() { + return true; + } + + public function supportsReadDataFromStdin() { + return false; + } + + protected function getMandatoryFlags() { + $options = array( + '--format=json', + '--force-exclusion', + '--no-color', + ); + + if ($this->config) { + $options[] = '--config='.$this->config; + } + + return $options; + } + + public function getLinterConfigurationOptions() { + $options = array( + 'rubocop.config' => array( + 'type' => 'optional string', + 'help' => pht('A custom configuration file.'), + ), + ); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function setLinterConfigurationValue($key, $value) { + switch ($key) { + case 'coffeelint.config': + $this->config = $value; + return; + } + + return parent::setLinterConfigurationValue($key, $value); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + $results = json_decode($stdout); + $messages = array(); + + foreach($results->files as $file) { + foreach($file->offenses as $offense) { + $message = new ArcanistLintMessage(); + + $message->setPath($file->path); + $message->setDescription($offense->message); + + $message->setLine($offense->location->line); + $message->setChar($offense->location->column); + + $message->setSeverity($this->getArcSeverity($offense->severity)); + $message->setName($this->getLinterName()); + $message->setCode($offense->cop_name); + + $messages[] = $message; + } + } + + return $messages; + } + + private function getArcSeverity($severity) { + $arc_severity = ArcanistLintSeverity::SEVERITY_ADVICE; + + switch($severity) { + case 'warning': + $arc_severity = ArcanistLintSeverity::SEVERITY_WARNING; + break; + + case 'error': + case 'fatal': + $arc_severity = ArcanistLintSeverity::SEVERITY_ERROR; + break; + } + + return $arc_severity; + } +} diff --git a/src/lint/linter/__tests__/ArcanistRuboCopLinterTestcase.php b/src/lint/linter/__tests__/ArcanistRuboCopLinterTestcase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/ArcanistRuboCopLinterTestcase.php @@ -0,0 +1,12 @@ +executeTestsInDirectory( + dirname(__FILE__).'/rubocop/', + new ArcanistRuboCopLinter()); + } + +} diff --git a/src/lint/linter/__tests__/rubocop/convention.lint-test b/src/lint/linter/__tests__/rubocop/convention.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/rubocop/convention.lint-test @@ -0,0 +1,4 @@ +def hello() +end +~~~~~~~~~~ +advice:1:11 diff --git a/src/lint/linter/__tests__/rubocop/error.lint-test b/src/lint/linter/__tests__/rubocop/error.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/rubocop/error.lint-test @@ -0,0 +1,4 @@ +def hello + puts 'world' +~~~~~~~~~~ +error:3:1 diff --git a/src/lint/linter/__tests__/rubocop/no_errors.lint-test b/src/lint/linter/__tests__/rubocop/no_errors.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/rubocop/no_errors.lint-test @@ -0,0 +1,4 @@ +def hello + puts 'hello world' +end +~~~~~~~~~~ diff --git a/src/lint/linter/__tests__/rubocop/warning.lint-test b/src/lint/linter/__tests__/rubocop/warning.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/rubocop/warning.lint-test @@ -0,0 +1,5 @@ +def hello(unused) + puts 'hi' +end +~~~~~~~~~ +warning:1:11