@title Arcanist User Guide: Lint @group userguide Guide to lint, linters, and linter configuration. 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}. = 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. See @{class@arcanist:ArcanistJSHintLinter}. - [[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}. - [[http://pear.php.net/package/PHP_CodeSniffer | PHP CodeSniffer]], a PHP linter. See @{class@arcanist:ArcanistPhpcsLinter}. - [[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}. 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}, 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. - @{class@arcanist:ArcanistMergeConflictLinter}, which detects unresolved merge conflicts. 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. = 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 `. 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}. = 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}.