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

Improve lint integration documentation

Summary: Explain what lint is, how to configure it, what we ship with, etc. Also wax philosophical.

Test Plan: Generated docs, read "carefully".

Reviewers: csilvers, btrahan, vrana

Reviewed By: vrana

CC: aran

Differential Revision: https://secure.phabricator.com/D2628
This commit is contained in:
epriestley 2012-06-01 07:55:30 -07:00
parent 3512ba2b55
commit fd20b18c2d
4 changed files with 206 additions and 16 deletions

View file

@ -9,7 +9,8 @@ Arcanist ships with a number of linters which you may want to reuse in whole
or in part in other projects. This document explains how to customize existing
linters for use in new engines.
First, you should set up a custom engine by following the steps in
First, you should set up an engine by following the instructions in
@{article:Arcanist User Guide: Lint} and possibly
@{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}.
Then, follow this guide to customize linters.
@ -17,7 +18,7 @@ Then, follow this guide to customize linters.
You should customize linters by configuring or composing them, not by extending
them -- their implementations are not necessarily stable. If a linter's
configuration options aren't flexible enough to meet your needs, a patch
configuration options aren't flexible enough to meet your needs, sending a patch
which improves its configurability is better than one that makes it
nonfinal.

View file

@ -0,0 +1,190 @@
@title Arcanist User Guide: Lint
@group userguide
Guide to lint, linters, and linter configuration.
= 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:
- [[http://www.jshint.com/ | JSHint]], a Javascript linter based on JSHint.
See @{class@arcanist:ArcanistJSHintLinter}.
- [[http://pypi.python.org/pypi/pep8 | PEP8]], a Python linter.
See @{class@arcanist:ArcanistPEP8Linter}.
- [[http://pypi.python.org/pypi/pyflakes | Pyflakes]], another Python linter.
See @{class@arcanist:ArcanistPyFlakesLinter}.
- [[http://pypi.python.org/pypi/pylint | Pylint]], yet another Python linter.
See @{class@arcanist:ArcanistPyLintLinter}.
- [[http://pear.php.net/package/PHP_CodeSniffer | PHP CodeSniffer]], a
PHP linter. See @{class@arcanist:ArcanistPhpcsLinter}.
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.
- @{class@arcanist:ArcanistLicenseLinter.php}, which can make sure license
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.
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.
= Configuring Lint =
Arcanist's lint integration 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}.
= 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.
= 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}.

View file

@ -5,7 +5,7 @@ Explains how to build new classes to control how Arcanist behaves.
= Overview =
Arcanist has some basic configuration options available in the ##.arcconfig##
Arcanist has some basic configuration options available in the `.arcconfig`
file (see @{article:Arcanist User Guide: Configuring a New Project}), but it
can't handle everything. If you want to customize Arcanist at a deeper level,
you need to build new classes. For instance:
@ -19,16 +19,16 @@ you need to build new classes. For instance:
@{class@arcanist:ArcanistConfiguration}.
Arcanist works through a sort of dependency-injection approach. For example,
Arcanist does not run lint rules by default, but you can set **lint_engine**
in your ##.arcconfig## to the name of a class which extends
Arcanist does not run lint rules by default, but you can set `lint.engine`
in your `.arcconfig` to the name of a class which extends
@{class@arcanist:ArcanistLintEngine}. When running from inside your project,
Arcanist will load this class and call methods on it in order to run lint. To
make this work, you need to do three things:
- actually write the class;
- add the library where the class exists to your ##.arcconfig##;
- add the class name to your ##.arcconfig## as the **lint_engine**,
**unit_engine**, or **arcanist_configuration**.
- add the class name to your ##.arcconfig## as the **lint.engine**,
**unit.engine**, or **arcanist_configuration**.
= Create a libphutil Library =
@ -38,12 +38,12 @@ then make the library loadable by adding it to your ##.arcconfig## like this:
{
// ...
"phutil_libraries" : {
"phutil_libraries" : [
// ...
"library-a" : "/path/to/my/library", // Absolute path
"library-b" : "support/arcanist", // Relative path in this project
"/path/to/my/library", // Absolute path
"support/arcanist", // Relative path in this project
// ...
}
]
// ...
}
@ -67,7 +67,7 @@ the appropriate configuration value.
{
// ...
"lint_engine" : "CustomArcanistLintEngine",
"lint.engine" : "CustomArcanistLintEngine",
// ...
}

View file

@ -76,11 +76,10 @@ For an exhaustive list of available options, see below.
Other options include:
- **lint_engine**: the name of a subclass of
- **lint.engine**: the name of a subclass of
@{class@arcanist:ArcanistLintEngine}, which should be used to apply lint
rules to this project. See @{article:Arcanist User Guide: Customizing Lint,
Unit Tests and Workflows}.
- **unit_engine**: the name of a subclass of
rules to this project. See @{article:Arcanist User Guide: Lint}.
- **unit.engine**: the name of a subclass of
@{class@arcanist:ArcanistBaseUnitTestEngine}, which should be used to apply
unit test rules to this project. See
@{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}.