1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 14:52:40 +01:00

Added RuboCop linter

Test Plan:
Add the following JSON to your `.arclint`:

```lang=json
"rubocop": {
  "type": "rubocop",
  "include": "(\\.rb$)"
}
```

Then, add a ruby file with errors, for instance:

```lang=ruby
def hello()
  puts 'world'
end
```

Run `arc lint`. It should come up with something along the line of: "Omit the parentheses in defs when the method doesn't accept any arguments."

Reviewers: joshuaspence, remon, #blessed_reviewers, epriestley

Reviewed By: joshuaspence, #blessed_reviewers, epriestley

Subscribers: reu, calfzhou, jjooss, cburroughs, chad, Korvin, epriestley

Differential Revision: https://secure.phabricator.com/D10738
This commit is contained in:
cburroughs 2015-04-06 09:11:06 -07:00 committed by epriestley
parent 3eacb08273
commit 41ddd34aeb
7 changed files with 151 additions and 0 deletions

View file

@ -161,6 +161,8 @@ phutil_register_library_map(array(
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php', 'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php', 'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php',
'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php',
'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php',
'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php',
'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php', 'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php',
'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php', 'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php',
@ -347,6 +349,8 @@ phutil_register_library_map(array(
'ArcanistRepositoryAPIMiscTestCase' => 'ArcanistTestCase', 'ArcanistRepositoryAPIMiscTestCase' => 'ArcanistTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'ArcanistTestCase', 'ArcanistRepositoryAPIStateTestCase' => 'ArcanistTestCase',
'ArcanistRevertWorkflow' => 'ArcanistWorkflow', 'ArcanistRevertWorkflow' => 'ArcanistWorkflow',
'ArcanistRuboCopLinter' => 'ArcanistExternalLinter',
'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinter' => 'ArcanistExternalLinter',
'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistScriptAndRegexLinter' => 'ArcanistLinter', 'ArcanistScriptAndRegexLinter' => 'ArcanistLinter',

View file

@ -0,0 +1,120 @@
<?php
final class ArcanistRuboCopLinter extends ArcanistExternalLinter {
private $config;
public function getInfoName() {
return 'RuboCop';
}
public function getInfoURI() {
return 'http://batsov.com/rubocop';
}
public function getInfoDescription() {
return pht(
'RuboCop is a Ruby static code analyzer, based on the community Ruby '.
'style guide.');
}
public function getLinterName() {
return 'RuboCop';
}
public function getLinterConfigurationName() {
return 'rubocop';
}
public function getDefaultBinary() {
return 'rubocop';
}
public function getVersion() {
list($stdout) = execx('%C --version', $this->getExecutableCommand());
$matches = array();
if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
return $matches['version'];
} else {
return false;
}
}
public function getInstallInstructions() {
return pht('Install RuboCop using `%s`.', 'gem install rubocop');
}
protected function getMandatoryFlags() {
$options = array(
'--format=json',
);
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 'rubocop.config':
$this->config = $value;
return;
}
return parent::setLinterConfigurationValue($key, $value);
}
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
$results = phutil_json_decode($stdout);
$messages = array();
foreach ($results['files'] as $file) {
foreach ($file['offenses'] as $offense) {
$message = id(new ArcanistLintMessage())
->setPath($file['path'])
->setDescription($offense['message'])
->setLine($offense['location']['line'])
->setChar($offense['location']['column'])
->setSeverity($this->getLintMessageSeverity($offense['severity']))
->setName($this->getLinterName())
->setCode($offense['cop_name']);
$messages[] = $message;
}
}
return $messages;
}
/*
Take the string from RuboCop's severity terminology and return an
ArcanistLintSeverity
*/
protected function getDefaultMessageSeverity($code) {
switch ($code) {
case 'convention':
case 'refactor':
case 'warning':
return ArcanistLintSeverity::SEVERITY_WARNING;
case 'error':
case 'fatal':
return ArcanistLintSeverity::SEVERITY_ERROR;
default:
return ArcanistLintSeverity::SEVERITY_ADVICE;
}
}
}

View file

@ -0,0 +1,10 @@
<?php
final class ArcanistRuboCopLinterTestCase
extends ArcanistExternalLinterTestCase {
public function testLinter() {
$this->executeTestsInDirectory(dirname(__FILE__).'/rubocop/');
}
}

View file

@ -0,0 +1,4 @@
def hello()
end
~~~~~~~~~~
warning:1:10

View file

@ -0,0 +1,4 @@
def hello
puts 'world'
~~~~~~~~~~
error:3:1

View file

@ -0,0 +1,4 @@
def hello
puts 'hello world'
end
~~~~~~~~~~

View file

@ -0,0 +1,5 @@
def hello(unused)
puts 'hi'
end
~~~~~~~~~
warning:1:11