2012-06-01 16:55:30 +02:00
|
|
|
@title Arcanist User Guide: Lint
|
|
|
|
@group userguide
|
|
|
|
|
|
|
|
Guide to lint, linters, and linter configuration.
|
|
|
|
|
2012-08-10 21:00:40 +02:00
|
|
|
This is a configuration guide that helps you set up advanced features. If you're
|
|
|
|
just getting started, you don't need to look at this yet. Instead, start with
|
|
|
|
the @{article:Arcanist User Guide}.
|
|
|
|
|
|
|
|
This guide explains how lint works when configured in an `arc` project. If
|
|
|
|
you haven't set up a project yet, do that first. For instructions, see
|
|
|
|
@{article:Arcanist User Guide: Configuring a New Project}.
|
|
|
|
|
2012-06-01 16:55:30 +02:00
|
|
|
= Overview =
|
|
|
|
|
|
|
|
"Lint" refers to a general class of programming tools which analyze source code
|
|
|
|
and raise warnings and errors about it. For example, a linter might raise
|
|
|
|
warnings about syntax errors, uses of undeclared variables, calls to deprecated
|
|
|
|
functions, spacing and formatting conventions, misuse of scope, implicit
|
|
|
|
fallthrough in switch statements, missing license headers, use of dangerous
|
|
|
|
language features, or a variety of other issues.
|
|
|
|
|
|
|
|
Integrating lint into your development pipeline has two major benefits:
|
|
|
|
|
|
|
|
- you can detect and prevent a large class of programming errors; and
|
|
|
|
- you can simplify code review by addressing many mechanical and formatting
|
|
|
|
problems automatically.
|
|
|
|
|
|
|
|
When arc is integrated with a lint toolkit, it enables the `arc lint` command
|
|
|
|
and runs lint on changes during `arc diff`. The user is prompted to fix errors
|
|
|
|
and warnings before sending their code for review, and lint issues which are
|
|
|
|
not fixed are visible during review.
|
|
|
|
|
|
|
|
There are many lint and static analysis tools available for a wide variety of
|
|
|
|
languages. Arcanist ships with bindings for many popular tools, and you can
|
|
|
|
write new bindings fairly easily if you have custom tools.
|
|
|
|
|
|
|
|
= Available Linters =
|
|
|
|
|
|
|
|
Arcanist ships with bindings for these linters:
|
|
|
|
|
2014-05-07 04:08:30 +02:00
|
|
|
- [[http://www.jshint.com/ | JSHint]], a Javascript linter.
|
2012-06-01 16:55:30 +02:00
|
|
|
See @{class@arcanist:ArcanistJSHintLinter}.
|
2014-05-07 04:08:30 +02:00
|
|
|
- [[http://pypi.python.org/pypi/pep8 | PEP8]],
|
|
|
|
[[http://pypi.python.org/pypi/pyflakes | Pyflakes]],
|
|
|
|
[[https://flake8.readthedocs.org/ | Flake8]], and
|
|
|
|
[[http://pypi.python.org/pypi/pylint | Pylint]] - various Python linters.
|
|
|
|
See @{class@arcanist:ArcanistPEP8Linter},
|
|
|
|
@{class@arcanist:ArcanistPyFlakesLinter},
|
|
|
|
@{class@arcanist:ArcanistFlake8Linter},
|
|
|
|
and @{class@arcanist:ArcanistPyLintLinter}.
|
2012-06-01 16:55:30 +02:00
|
|
|
- [[http://pear.php.net/package/PHP_CodeSniffer | PHP CodeSniffer]], a
|
|
|
|
PHP linter. See @{class@arcanist:ArcanistPhpcsLinter}.
|
2014-05-07 04:08:30 +02:00
|
|
|
- [[http://cppcheck.sourceforge.net/ | Cppcheck]] and
|
|
|
|
[[http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py |
|
|
|
|
cpplint.py]], two checkers for C++. See
|
|
|
|
@{class@arcanist:ArcanistCppcheckLinter} and
|
|
|
|
@{class@arcanist:ArcanistCpplintLinter} respectively.
|
|
|
|
- [[https://github.com/hach-que/cstools | cslint]], a C# linting tool based on
|
|
|
|
[[http://stylecop.codeplex.com/ | StyleCop]]. See
|
|
|
|
@{class@arcanist:ArcanistCSharpLinter}.
|
|
|
|
- [[http://puppet-lint.com/ | puppet-lint]], a linter for
|
|
|
|
[[http://puppetlabs.com/ | Puppet]] manifests. See
|
|
|
|
@{class@arcanist:ArcanistPuppetLintLinter}.
|
|
|
|
- [[http://www.ruby-lang.org | Ruby]]'s built-in checker. See
|
|
|
|
@{class@arcanist:ArcanistRubyLinter}.
|
|
|
|
- [[http://www.scala-sbt.org/ | sbt]], a built tool for Scala. See
|
|
|
|
@{class@arcanist:ArcanistScalaSBTLinter}.
|
|
|
|
- [[http://csslint.net/ | CSSLint]], for CSS:
|
|
|
|
@{class@arcanist:ArcanistCSSLintLinter}.
|
|
|
|
- [[https://github.com/less/less.js | lessc]], error detection in
|
|
|
|
[[http://lesscss.org/ | LESS]] code. @{class@arcanist:ArcanistLesscLinter}.
|
|
|
|
- [[http://php.net/simplexml | SimpleXML]] for XML files.
|
|
|
|
@{class@arcanist:ArcanistXMLLinter}.
|
|
|
|
|
2012-06-01 16:55:30 +02:00
|
|
|
|
|
|
|
Arcanist also ships with generic bindings which can be configured to parse the
|
|
|
|
output of a broad range of lint programs:
|
|
|
|
|
|
|
|
- @{class@arcanist:ArcanistScriptAndRegexLinter}, which runs a script and
|
|
|
|
parses its output with a regular expression.
|
|
|
|
- @{class@arcanist:ArcanistConduitLinter}, which invokes a linter over
|
|
|
|
Conduit and can allow you to build client/server linters.
|
|
|
|
|
|
|
|
Additionally, Arcanist ships with some general purpose linters:
|
|
|
|
|
|
|
|
- @{class@arcanist:ArcanistTextLinter}, which enforces basic things like
|
|
|
|
trailing whitespace, DOS newlines, file encoding, line widths, terminal
|
|
|
|
newlines, and tab literals.
|
|
|
|
- @{class@arcanist:ArcanistSpellingLinter}, which can detect common spelling
|
|
|
|
mistakes.
|
|
|
|
- @{class@arcanist:ArcanistFilenameLinter}, which can enforce generally
|
|
|
|
sensible rules about not giving files nonsense names.
|
2012-08-10 23:21:51 +02:00
|
|
|
- @{class@arcanist:ArcanistLicenseLinter}, which can make sure license
|
2012-06-01 16:55:30 +02:00
|
|
|
headers are applied to all source files.
|
|
|
|
- @{class@arcanist:ArcanistNoLintLinter}, which can disable lint for files
|
|
|
|
marked unlintable.
|
|
|
|
- @{class@arcanist:ArcanistGeneratedLinter}, which can disable lint for
|
|
|
|
generated files.
|
2014-05-07 04:08:30 +02:00
|
|
|
- @{class@arcanist:ArcanistMergeConflictLinter}, which detects unresolved
|
|
|
|
merge conflicts.
|
2012-06-01 16:55:30 +02:00
|
|
|
|
|
|
|
Finally, Arcanist has special-purpose linters:
|
|
|
|
|
|
|
|
- @{class@arcanist:ArcanistXHPASTLinter}, the PHP linter used by Phabricator
|
|
|
|
itself. This linter is powerful, but somewhat rigid (it enforces phutil
|
|
|
|
rules and isn't very configurable for other rulesets).
|
|
|
|
- @{class@arcanist:ArcanistPhutilLibraryLinter}, which enforces phutil library
|
|
|
|
layout rules.
|
|
|
|
|
|
|
|
You can add support for new linters in three ways:
|
|
|
|
|
|
|
|
- write new bindings and contribute them to the upstream;
|
|
|
|
- write new bindings and install them alongside Arcanist; or
|
|
|
|
- use a generic binding like @{class@arcanist:ArcanistScriptAndRegexLinter}
|
|
|
|
and drive the integration through configuration.
|
|
|
|
|
|
|
|
= Using Lint to Improve Code Review =
|
|
|
|
|
|
|
|
Code review is most valuable when it's about the big ideas in a change. It is
|
|
|
|
substantially less valuable when it devolves into nitpicking over style,
|
|
|
|
formatting, and naming conventions.
|
|
|
|
|
|
|
|
The best response to receiving a review request full of style problems is
|
|
|
|
probably to reject it immediately, point the author at your coding convention
|
|
|
|
documentation, and ask them to fix it before sending it for review. But even
|
|
|
|
this is a pretty negative experience for both parties, and less experienced
|
|
|
|
reviewers sometimes go through the whole review and point out every problem
|
|
|
|
individually.
|
|
|
|
|
|
|
|
Lint can greatly reduce the negativity of this whole experience (and the amount
|
|
|
|
of time wasted arguing about these things) by enforcing style and formatting
|
|
|
|
rules automatically. Arcanist supports linters that not only raise warnings
|
|
|
|
about these problems, but provide patches and fix the problems for the author --
|
|
|
|
before the code goes to review.
|
|
|
|
|
|
|
|
Good linter integration means that code is pretty much mechanically correct by
|
|
|
|
the time any reviewer sees it, provides clear rules about style which are
|
|
|
|
especially helpful to new authors, and has the overall effect of pushing
|
|
|
|
discussion away from stylistic nitpicks and toward useful examination of large
|
|
|
|
ideas.
|
|
|
|
|
|
|
|
It can also provide a straightforward solution to arguments about style:
|
|
|
|
|
|
|
|
- If a rule is important enough that it should be enforced, the proponent must
|
|
|
|
add it to lint so it is automatically detected or fixed in the future and
|
|
|
|
no one has to argue about it ever again.
|
|
|
|
- If it's not important enough for them to do the legwork to add it to lint,
|
|
|
|
they have to stop complaining about it.
|
|
|
|
|
|
|
|
This may or may not be an appropriate methodology to adopt at your organization,
|
|
|
|
but it generally puts the incentives in the right places.
|
|
|
|
|
|
|
|
= Philosophy of Lint =
|
|
|
|
|
|
|
|
Some general thoughts on how to develop lint effectively, based on building
|
|
|
|
lint tools at Facebook:
|
|
|
|
|
|
|
|
- Don't write regex-based linters to enforce language rules. Use a real parser
|
|
|
|
or AST-based tool. This is not a domain you can get right at any nontrivial
|
|
|
|
complexity with raw regexes. That is not a challenge. Just don't do this.
|
|
|
|
- False positives are pretty bad and should be avoided. You should aim to
|
|
|
|
implement only rules that have very few false positives, and provide ways to
|
|
|
|
mark false positives as OK. If running lint always raises 30 warnings about
|
|
|
|
irrelevant nonsense, it greatly devalues the tool.
|
|
|
|
- Move toward autocorrect rules. Most linters do not automatically correct
|
|
|
|
the problems they detect, but Arcanist supports this and it's quite
|
|
|
|
valuable to have the linter not only say "the convention is to put a space
|
|
|
|
after comma in a function call" but to fix it for you.
|
|
|
|
|
2014-05-07 04:08:30 +02:00
|
|
|
|
|
|
|
= Configuring Lint =
|
|
|
|
|
|
|
|
Most built in linters can be setup by creating a file named `.arclint` in the
|
|
|
|
workspace root, which is read by
|
|
|
|
@{class@arcanist:ArcanistConfigurationDrivenLintEngine}.
|
|
|
|
|
|
|
|
It's a JSON file, which should look something like:
|
|
|
|
{
|
|
|
|
"linters" : {
|
|
|
|
"sample" : {
|
|
|
|
"type" : "pep8",
|
|
|
|
"include" : "(\\.py$)",
|
|
|
|
"exclude" : "(^exclude.py)",
|
|
|
|
"severity" : {
|
|
|
|
"E401" : "warning"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Where:
|
|
|
|
- The key (`sample`) is only used for reporting,
|
|
|
|
- `type` (`pep8`) is used to find the right linter,
|
|
|
|
- `include` and `exclude` specify paths to run linter on, and
|
|
|
|
- `severity` is specified by the linter implementation; Other linters may
|
|
|
|
define other configurable values.
|
|
|
|
|
|
|
|
You may specify many linters in a single `.arclint` file; For an example, see
|
|
|
|
[[https://github.com/epriestley/arclint-examples/ | the arclint-examples
|
|
|
|
repository]].
|
|
|
|
|
|
|
|
Currently available linter types are: `csharp`, `filename`, `csslint`,
|
|
|
|
`flake8`, `jshint`, `jsonlint`, `lessc`, `pep8`, `phpcs`, `puppet-lint`,
|
|
|
|
`pyflakes`, `ruby`, `generated`, `nolint`, `script-and-regex`,
|
|
|
|
`spelling`, `text`, `xml`.
|
|
|
|
|
|
|
|
= Legacy Setups: Using arcconfig =
|
|
|
|
|
|
|
|
Integration with linters that do not (yet) fully support `.arclint` involves two
|
|
|
|
major components: linters and lint engines.
|
|
|
|
|
|
|
|
Linters themselves are programs which detect problems in a source file. Usually
|
|
|
|
a linter is an external script, which Arcanist runs and passes a path to, like
|
|
|
|
`jshint` or `pep8.py`. The script emits some messages, and Arcanist parses
|
|
|
|
the output into structured errors. A piece of glue code (like
|
|
|
|
@{class@arcanist:ArcanistJSHintLinter} or
|
|
|
|
@{class@arcanist:ArcanistPEP8Linter}) handles calling the external script and
|
|
|
|
interpreting its output.
|
|
|
|
|
|
|
|
Lint engines coordinate linters, and decide which linters should run on which
|
|
|
|
files. For instance, you might want to run `jshint` on all your `.js` files,
|
|
|
|
and `pep8.py` on all your `.py` files. And you might not want to lint anything
|
|
|
|
in `externals/` or `third-party/`, and maybe there are other files which you
|
|
|
|
want to exclude or apply special rules for.
|
|
|
|
|
|
|
|
To configure arc for lint, you specify the name of a lint engine, and possibly
|
|
|
|
provide some additional configuration. To name a lint engine, set `lint.engine`
|
|
|
|
in your `.arcconfig` to the name of a class which extends
|
|
|
|
@{class@arcanist:ArcanistLintEngine}. For more information on `.arcconfig`, see
|
|
|
|
@{article:Arcanist User Guide: Configuring a New Project}.
|
|
|
|
|
|
|
|
You can also set a default lint engine by setting `lint.engine` in your global
|
|
|
|
user config with `arc set-config lint.engine`, or specify one explicitly with
|
|
|
|
`arc lint --engine <engine>`.
|
|
|
|
|
|
|
|
The available engines are:
|
|
|
|
|
|
|
|
- @{class@arcanist:ComprehensiveLintEngine}, which runs a wide array of
|
|
|
|
linters on many types of files. This is probably of limited use in any real
|
|
|
|
project because it is overbroad, but is a good starting point for getting
|
|
|
|
lint doing things.
|
|
|
|
- @{class@arcanist:ArcanistSingleLintEngine}, which runs a single linter on
|
|
|
|
every file unconditionally. This can be used with a glue linter like
|
|
|
|
@{class@arcanist:ArcanistScriptAndRegexLinter} to put engine logic in an
|
|
|
|
external script.
|
|
|
|
- A custom engine you write. For most projects, lint rules are sufficiently
|
|
|
|
specialized that this is the best option. For instructions on writing a
|
|
|
|
custom lint engine, see
|
|
|
|
@{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}
|
|
|
|
and @{class@arcanist:ExampleLintEngine}.
|
|
|
|
|
|
|
|
|
2012-06-01 16:55:30 +02:00
|
|
|
= Next Steps =
|
|
|
|
|
|
|
|
Continue by:
|
|
|
|
|
|
|
|
- integrating and customizing built-in linters and lint bindings with
|
|
|
|
@{article:Arcanist User Guide: Customizing Existing Linters}; or
|
|
|
|
- learning how to add new linters and lint engines with
|
|
|
|
@{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}.
|