1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-29 10:12:41 +01:00

Expose PEP8, Flake8 and CSSLint engines to .arclint

Summary:
Ref T3186. Ref T2039. Allow these linters to be selected with `.arclint`.

Also allow severities to be set.

Also fix some other minor bugs.

Test Plan:
https://github.com/epriestley/arclint-examples
https://github.com/epriestley/arclint-examples/blob/master/.arclint

Reviewers: btrahan

Reviewed By: btrahan

CC: chad, aran

Maniphest Tasks: T2039, T3186

Differential Revision: https://secure.phabricator.com/D6803
This commit is contained in:
epriestley 2013-08-23 11:58:07 -07:00
parent 6e5be59ad6
commit 1f3cb63db2
7 changed files with 147 additions and 24 deletions

View file

@ -65,13 +65,14 @@ final class ArcanistConfigurationDrivenLintEngine extends ArcanistLintEngine {
foreach ($more as $key => $value) {
if (array_key_exists($key, $spec)) {
try {
$linter->setLinterConfigurationValue($key, $spec);
$linter->setLinterConfigurationValue($key, $spec[$key]);
} catch (Exception $ex) {
$message = pht(
'Error in parsing ".arclint" file, in key "%s" for '.
'linter "%s".',
'linter "%s": %s',
$key,
$name);
$name,
$ex->getMessage());
throw new PhutilProxyException($message, $ex);
}
}

View file

@ -19,6 +19,10 @@ final class ArcanistCSSLintLinter extends ArcanistExternalLinter {
return 'CSSLint';
}
public function getLinterConfigurationName() {
return 'csslint';
}
public function getMandatoryFlags() {
return '--format=lint-xml';
}
@ -65,7 +69,7 @@ final class ArcanistCSSLintLinter extends ArcanistExternalLinter {
$data = $this->getData($path);
$lines = explode("\n", $data);
$name = $this->getLinterName() . ' - ' . $child->getAttribute('reason');
$name = $child->getAttribute('reason');
$severity = ($child->getAttribute('severity') == 'warning')
? ArcanistLintSeverity::SEVERITY_WARNING
: ArcanistLintSeverity::SEVERITY_ERROR;
@ -74,11 +78,8 @@ final class ArcanistCSSLintLinter extends ArcanistExternalLinter {
$message->setPath($path);
$message->setLine($child->getAttribute('line'));
$message->setChar($child->getAttribute('char'));
$message->setCode($child->getAttribute('severity'));
$message->setName($name);
$message->setDescription(
$child->getAttribute('reason').
"\nEvidence:".$child->getAttribute('evidence'));
$message->setCode('CSSLint');
$message->setDescription($child->getAttribute('reason'));
$message->setSeverity($severity);
if ($child->hasAttribute('line')) {
@ -93,4 +94,20 @@ final class ArcanistCSSLintLinter extends ArcanistExternalLinter {
return $messages;
}
protected function getLintCodeFromLinterConfigurationKey($code) {
// NOTE: We can't figure out which rule generated each message, so we
// can not customize severities. I opened a pull request to add this
// ability; see:
//
// https://github.com/stubbornella/csslint/pull/409
throw new Exception(
pht(
"CSSLint does not currently support custom severity levels, because ".
"rules can't be identified from messages in output. ".
"See Pull Request #409."));
}
}

View file

@ -393,8 +393,12 @@ abstract class ArcanistExternalLinter extends ArcanistFutureLinter {
$messages = $this->parseLinterOutput($path, $err, $stdout, $stderr);
if ($messages === false) {
$future->resolvex();
return;
if ($err) {
$future->resolvex();
} else {
throw new Exception(
"Linter failed to parse output!\n\n{$stdout}\n\n{$stderr}");
}
}
foreach ($messages as $message) {
@ -407,6 +411,7 @@ abstract class ArcanistExternalLinter extends ArcanistFutureLinter {
$options = array(
'bin' => 'optional string | list<string>',
'flags' => 'optional string',
'severity' => 'optional map<string, string>',
);
if ($this->shouldUseInterpreter()) {
@ -464,10 +469,50 @@ abstract class ArcanistExternalLinter extends ArcanistFutureLinter {
if (strlen($value)) {
$this->setFlags($value);
}
break;
return;
case 'severity':
$sev_map = array(
'error' => ArcanistLintSeverity::SEVERITY_ERROR,
'warning' => ArcanistLintSeverity::SEVERITY_WARNING,
'autofix' => ArcanistLintSeverity::SEVERITY_AUTOFIX,
'advice' => ArcanistLintSeverity::SEVERITY_ADVICE,
'disabled' => ArcanistLintSeverity::SEVERITY_DISABLED,
);
$custom = array();
foreach ($value as $code => $severity) {
if (empty($sev_map[$severity])) {
$valid = implode(', ', array_keys($sev_map));
throw new Exception(
pht(
'Unknown lint severity "%s". Valid severities are: %s.',
$severity,
$valid));
}
$code = $this->getLintCodeFromLinterConfigurationKey($code);
$custom[$code] = $severity;
}
$this->setCustomSeverityMap($custom);
return;
}
return parent::setLinterConfigurationValue($key, $value);
}
/**
* Map a configuration lint code to an `arc` lint code. Primarily, this is
* intended for validation, but can also be used to normalize case or
* otherwise be more permissive in accepted inputs.
*
* If the code is not recognized, you should throw an exception.
*
* @param string Code specified in configuration.
* @return string Normalized code to use in severity map.
*/
protected function getLintCodeFromLinterConfigurationKey($code) {
return $code;
}
}

View file

@ -12,6 +12,10 @@ final class ArcanistFlake8Linter extends ArcanistExternalLinter {
return 'flake8';
}
public function getLinterConfigurationName() {
return 'flake8';
}
public function getDefaultFlags() {
// TODO: Deprecated.
@ -66,12 +70,6 @@ final class ArcanistFlake8Linter extends ArcanistExternalLinter {
$matches[$key] = trim($match);
}
if (substr($matches[4], 0, 1) == 'E') {
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
} else {
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
}
$message = new ArcanistLintMessage();
$message->setPath($path);
$message->setLine($matches[2]);
@ -81,7 +79,7 @@ final class ArcanistFlake8Linter extends ArcanistExternalLinter {
$message->setCode($matches[4]);
$message->setName($this->getLinterName().' '.$matches[3]);
$message->setDescription($matches[5]);
$message->setSeverity($severity);
$message->setSeverity($this->getLintMessageSeverity($matches[4]));
$messages[] = $message;
}
@ -93,4 +91,34 @@ final class ArcanistFlake8Linter extends ArcanistExternalLinter {
return $messages;
}
protected function getDefaultMessageSeverity($code) {
if (preg_match('/^C/', $code)) {
// "C": Cyclomatic complexity
return ArcanistLintSeverity::SEVERITY_ADVICE;
} else if (preg_match('/^W/', $code)) {
// "W": PEP8 Warning
return ArcanistLintSeverity::SEVERITY_WARNING;
} else {
// "E": PEP8 Error
// "F": PyFlakes Error
return ArcanistLintSeverity::SEVERITY_ERROR;
}
}
protected function getLintCodeFromLinterConfigurationKey($code) {
if (!preg_match('/^(E|W|C|F)\d+$/', $code)) {
throw new Exception(
pht(
'Unrecognized lint message code "%s". Expected a valid flake8 '.
'lint code like "%s", or "%s", or "%s", or "%s".',
$code,
"E225",
"W291",
"F811",
"C901"));
}
return $code;
}
}

View file

@ -121,6 +121,10 @@ abstract class ArcanistLinter {
return $map[$code];
}
return $this->getDefaultMessageSeverity($code);
}
protected function getDefaultMessageSeverity($code) {
return ArcanistLintSeverity::SEVERITY_ERROR;
}

View file

@ -11,6 +11,10 @@ final class ArcanistPEP8Linter extends ArcanistExternalLinter {
return 'PEP8';
}
public function getLinterConfigurationName() {
return 'pep8';
}
public function getCacheVersion() {
list($stdout) = execx('%C --version', $this->getExecutableCommand());
return $stdout.$this->getCommandFlags();
@ -76,9 +80,6 @@ final class ArcanistPEP8Linter extends ArcanistExternalLinter {
foreach ($matches as $key => $match) {
$matches[$key] = trim($match);
}
if (!$this->isMessageEnabled($matches[4])) {
continue;
}
$message = new ArcanistLintMessage();
$message->setPath($path);
$message->setLine($matches[2]);
@ -86,7 +87,7 @@ final class ArcanistPEP8Linter extends ArcanistExternalLinter {
$message->setCode($matches[4]);
$message->setName('PEP8 '.$matches[4]);
$message->setDescription($matches[5]);
$message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
$message->setSeverity($this->getLintMessageSeverity($matches[4]));
$messages[] = $message;
}
@ -98,4 +99,31 @@ final class ArcanistPEP8Linter extends ArcanistExternalLinter {
return $messages;
}
protected function getDefaultMessageSeverity($code) {
if (preg_match('/^W/', $code)) {
return ArcanistLintSeverity::SEVERITY_WARNING;
} else {
// TODO: Once severities/.arclint are more usable, restore this to
// "ERROR".
// return ArcanistLintSeverity::SEVERITY_ERROR;
return ArcanistLintSeverity::SEVERITY_WARNING;
}
}
protected function getLintCodeFromLinterConfigurationKey($code) {
if (!preg_match('/^(E|W)\d+$/', $code)) {
throw new Exception(
pht(
'Unrecognized lint message code "%s". Expected a valid PEP8 '.
'lint code like "%s" or "%s".',
$code,
"E101",
"W291"));
}
return $code;
}
}

View file

@ -4,4 +4,4 @@ def hello():
return foo
~~~~~~~~~~
error:3:1
warning:4:1
error:4:1