1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 05:50:54 +01:00

Upgrade C# linter to support linting multiple files at a time

Summary:
This upgrades `cslint` to support linting multiple files at a time.  As this required a backwards-incompatible to `cslint`, I've added a SUPPORTED_VERSION constant which can be used to detect these kinds of breaking changes in the future (and prompt users to upgrade the `cslint` they have in their repository).

The reason for this upgrade is mainly around running `arc lint --everything`, where there are significant performance benefits gained when bulk linting lots of files per command execution.

Test Plan: Upgraded `cslint` in the Tychaia repository and ran `arc lint --everything --trace`.  Saw a substantially less number of executions happening for linting and all of the results came through as expected.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran, waynea

Differential Revision: https://secure.phabricator.com/D7599
This commit is contained in:
James Rhodes 2013-11-18 11:32:58 -08:00 committed by epriestley
parent 19edbbb46e
commit 61753b6770

View file

@ -5,13 +5,16 @@
* *
* @group linter * @group linter
*/ */
final class ArcanistCSharpLinter extends ArcanistFutureLinter { final class ArcanistCSharpLinter extends ArcanistLinter {
private $runtimeEngine; private $runtimeEngine;
private $cslintEngine; private $cslintEngine;
private $cslintHintPath; private $cslintHintPath;
private $loaded; private $loaded;
private $discoveryMap; private $discoveryMap;
private $futures;
const SUPPORTED_VERSION = 1;
public function getLinterName() { public function getLinterName() {
return 'C#'; return 'C#';
@ -101,74 +104,135 @@ final class ArcanistCSharpLinter extends ArcanistFutureLinter {
throw new Exception("Unable to locate cslint."); throw new Exception("Unable to locate cslint.");
} }
// Determine cslint version.
$ver_future = new ExecFuture(
"%C -v",
$this->runtimeEngine.$this->cslintEngine);
list($err, $stdout, $stderr) = $ver_future->resolve();
if ($err !== 0) {
throw new Exception(
"You are running an old version of cslint. Please ".
"upgrade to version ".self::SUPPORTED_VERSION.".");
}
$ver = (int)$stdout;
if ($ver < self::SUPPORTED_VERSION) {
throw new Exception(
"You are running an old version of cslint. Please ".
"upgrade to version ".self::SUPPORTED_VERSION.".");
} elseif ($ver > self::SUPPORTED_VERSION) {
throw new Exception(
"Arcanist does not support this version of cslint (it is ".
"newer). You can try upgrading Arcanist with `arc upgrade`.");
}
$this->loaded = true; $this->loaded = true;
} }
protected function buildFutures(array $paths) { public function lintPath($path) {
}
public function willLintPaths(array $paths) {
$this->loadEnvironment(); $this->loadEnvironment();
$futures = array(); $futures = array();
// Bulk linting up into futures, where the number of files
// is based on how long the command is.
$current_paths = array();
foreach ($paths as $path) { foreach ($paths as $path) {
// %s won't pass through the JSON correctly // If the current paths for the command, plus the next path
// under Windows. This is probably because not only // is greater than 6000 characters (less than the Windows
// does the JSON have quotation marks in the content, // command line limit), then finalize this future and add it.
// but because there'll be a lot of escaping and $total = 0;
// double escaping because the JSON also contains foreach ($current_paths as $current_path) {
// regular expressions. cslint supports passing the $total += strlen($current_path) + 3; // Quotes and space.
// settings JSON through base64-encoded to mitigate }
// this issue. if ($total + strlen($path) > 6000) {
$futures[$path] = new ExecFuture( // %s won't pass through the JSON correctly
"%C --settings-base64=%s -r=. %s", // under Windows. This is probably because not only
// does the JSON have quotation marks in the content,
// but because there'll be a lot of escaping and
// double escaping because the JSON also contains
// regular expressions. cslint supports passing the
// settings JSON through base64-encoded to mitigate
// this issue.
$futures[] = new ExecFuture(
"%C --settings-base64=%s -r=. %Ls",
$this->runtimeEngine.$this->cslintEngine,
base64_encode(json_encode($this->discoveryMap)),
$current_paths);
$current_paths = array();
}
// Append the path to the current paths array.
$current_paths[] = $this->getEngine()->getFilePathOnDisk($path);
}
// If we still have paths left in current paths, then we need to create
// a future for those too.
if (count($current_paths) > 0) {
$futures[] = new ExecFuture(
"%C --settings-base64=%s -r=. %Ls",
$this->runtimeEngine.$this->cslintEngine, $this->runtimeEngine.$this->cslintEngine,
base64_encode(json_encode($this->discoveryMap)), base64_encode(json_encode($this->discoveryMap)),
$this->getEngine()->getFilePathOnDisk($path)); $current_paths);
$current_paths = array();
} }
return $futures; $this->futures = $futures;
} }
protected function resolveFuture($path, Future $future) { public function didRunLinters() {
list($rc, $stdout) = $future->resolve(); if ($this->futures) {
$results = json_decode($stdout); foreach (Futures($this->futures) as $future) {
if ($results === null || $results->Issues === null) { $this->resolveFuture($future);
return; }
} }
foreach ($results->Issues as $issue) { }
$message = new ArcanistLintMessage();
$message->setPath($path); protected function resolveFuture(Future $future) {
$message->setLine($issue->LineNumber); list($stdout) = $future->resolvex();
$message->setCode($issue->Index->Code); $all_results = json_decode($stdout);
$message->setName($issue->Index->Name); foreach ($all_results as $results) {
$message->setChar($issue->Column); if ($results === null || $results->Issues === null) {
$message->setOriginalText($issue->OriginalText); return;
$message->setReplacementText($issue->ReplacementText);
$message->setDescription(
vsprintf($issue->Index->Message, $issue->Parameters));
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
switch ($issue->Index->Severity) {
case 0:
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
break;
case 1:
$severity = ArcanistLintSeverity::SEVERITY_AUTOFIX;
break;
case 2:
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
break;
case 3:
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
break;
case 4:
$severity = ArcanistLintSeverity::SEVERITY_DISABLED;
break;
} }
$severity_override = $this->getLintMessageSeverity($issue->Index->Code); foreach ($results->Issues as $issue) {
if ($severity_override !== null) { $message = new ArcanistLintMessage();
$severity = $severity_override; $message->setPath($results->FileName);
$message->setLine($issue->LineNumber);
$message->setCode($issue->Index->Code);
$message->setName($issue->Index->Name);
$message->setChar($issue->Column);
$message->setOriginalText($issue->OriginalText);
$message->setReplacementText($issue->ReplacementText);
$message->setDescription(
vsprintf($issue->Index->Message, $issue->Parameters));
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
switch ($issue->Index->Severity) {
case 0:
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
break;
case 1:
$severity = ArcanistLintSeverity::SEVERITY_AUTOFIX;
break;
case 2:
$severity = ArcanistLintSeverity::SEVERITY_WARNING;
break;
case 3:
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
break;
case 4:
$severity = ArcanistLintSeverity::SEVERITY_DISABLED;
break;
}
$severity_override = $this->getLintMessageSeverity($issue->Index->Code);
if ($severity_override !== null) {
$severity = $severity_override;
}
$message->setSeverity($severity);
$this->addLintMessage($message);
} }
$message->setSeverity($severity);
$this->addLintMessage($message);
} }
} }