2012-01-20 02:09:27 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements linting via Conduit RPC call.
|
|
|
|
* Slow by definition, but allows sophisticated linting that relies on
|
|
|
|
* stuff like big indexes of a codebase.
|
|
|
|
* Recommended usage is to gate these to the advice lint level.
|
|
|
|
*
|
|
|
|
* The conduit endpoint should implement a method named the same as
|
|
|
|
* the value of ArcanistConduitLinter::CONDUIT_METHOD.
|
|
|
|
*
|
|
|
|
* It takes an array with a key 'file_contents' which is an array mapping
|
|
|
|
* file paths to their complete contents.
|
|
|
|
*
|
|
|
|
* It should return an array mapping those same paths to arrays describing the
|
|
|
|
* lint for each path.
|
|
|
|
*
|
|
|
|
* The lint for a path is described as a list of structured dictionaries.
|
|
|
|
*
|
|
|
|
* The dictionary structure is effectively defined by
|
|
|
|
* ArcanistLintMessage::newFromDictionary.
|
|
|
|
*
|
|
|
|
* Effective keys are:
|
|
|
|
* 'path' => must match passed in path.
|
|
|
|
* 'line'
|
|
|
|
* 'char'
|
|
|
|
* 'code'
|
|
|
|
* 'severity' => Must match a constant in ArcanistLintSeverity.
|
|
|
|
* 'name'
|
|
|
|
* 'description'
|
|
|
|
* 'original' & 'replacement' => optional patch information
|
2013-01-11 00:03:33 +01:00
|
|
|
* 'locations' => other locations of the same error (in the same format)
|
2012-01-20 02:09:27 +01:00
|
|
|
*
|
|
|
|
* This class is intended for customization via instantiation, not via
|
|
|
|
* subclassing.
|
|
|
|
*/
|
2012-01-31 21:07:05 +01:00
|
|
|
final class ArcanistConduitLinter extends ArcanistLinter {
|
2012-01-20 02:09:27 +01:00
|
|
|
const CONDUIT_METHOD = 'lint.getalllint';
|
|
|
|
|
|
|
|
private $conduitURI;
|
|
|
|
private $linterName;
|
|
|
|
private $lintByPath; // array(/pa/th/ => <lint>), valid after willLintPaths().
|
|
|
|
|
Lay groundwork for configuration-driven linters
Summary:
Ref T2039. That task has a bunch of discussion, but basically we do a poor job of serving the midrange of lint configuration right now.
If you have something simple, the default linters work.
If you have something complex, building your own engine lets you do whatever you want.
But many users want something in between, which isn't really well accommodated. The idea is to let you write a `.arclint` file, which looks something like this:
{
"linters" : {
"css" : {
"type" : "csslint",
"include" : "(\.css$)",
"exclude" : "(^externals/)",
"bin" : "/usr/local/bin/csslint"
},
"js" : {
"type" : "jshint",
"include" : "(\.js$)",
"exclude" : "(^externals/)",
"bin" : "support/bin/jshint",
"interpreter" : "/usr/local/bin/node"
}
}
}
...which will provide a bunch of common options around lint severity, interpreter and binary locaitons, included and excluded files, etc.
This implements some basics, and very rough support in the Filename linter.
Test Plan:
Generated a `.arclint` file and saw it apply filename lint correctly. Used `debug` mode and tried invalid regexps.
{
"debug" : true,
"linters" : {
"filename" : {
"type" : "filename",
"exclude" : ["@^externals/@"]
}
}
}
Next steps include:
- Provide an external linter archetype (T3186) and expose a common set of configuration here ("bin", "interpreter", "flags", "severity").
- Provide a `.arcunit` file which works similarly (it can probably be simpler).
Reviewers: btrahan, Firehed
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2039
Differential Revision: https://secure.phabricator.com/D6797
2013-08-23 01:02:16 +02:00
|
|
|
public function __construct($conduit_uri = null, $linter_name = null) {
|
|
|
|
|
|
|
|
// TODO: Facebook uses this (probably?) but we need to be able to
|
|
|
|
// construct it without arguments for ".arclint".
|
|
|
|
|
2012-01-20 02:09:27 +01:00
|
|
|
$this->conduitURI = $conduit_uri;
|
|
|
|
$this->linterName = $linter_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function willLintPaths(array $paths) {
|
|
|
|
// Load all file path data into $this->data.
|
|
|
|
array_map(array($this, 'getData'), $paths);
|
|
|
|
|
|
|
|
$conduit = new ConduitClient($this->conduitURI);
|
|
|
|
|
|
|
|
$this->lintByPath = $conduit->callMethodSynchronous(
|
|
|
|
self::CONDUIT_METHOD,
|
|
|
|
array(
|
|
|
|
'file_contents' => $this->data,
|
2013-02-19 23:09:20 +01:00
|
|
|
));
|
2012-01-20 02:09:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function lintPath($path) {
|
|
|
|
$lint_for_path = idx($this->lintByPath, $path);
|
|
|
|
if (!$lint_for_path) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($lint_for_path as $lint) {
|
|
|
|
$this->addLintMessage(ArcanistLintMessage::newFromDictionary($lint));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getLinterName() {
|
|
|
|
return $this->linterName;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getLintSeverityMap() {
|
|
|
|
// The rationale here is that this class will only be used for custom
|
|
|
|
// linting in installations. No two server endpoints will be the same across
|
|
|
|
// different instantiations. Therefore, the server can handle all severity
|
|
|
|
// customization directly.
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
'ArcanistConduitLinter does not support client-side severity '.
|
|
|
|
'customization.'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getLintNameMap() {
|
|
|
|
// See getLintSeverityMap for rationale.
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
'ArcanistConduitLinter does not support a name map.'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|