@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Further modernize lint documentation

Summary: Ref T2039. Contains a small amount of wishful thinking, I'll note it inline.

Test Plan: Generated and read documentation.

Reviewers: btrahan, avive, joshuaspence

Reviewed By: joshuaspence

Subscribers: epriestley, avivey

Maniphest Tasks: T2039

Differential Revision: https://secure.phabricator.com/D9061

+311 -153
+311 -153
src/docs/user/userguide/arcanist_lint.diviner
··· 11 11 you haven't set up a project yet, do that first. For instructions, see 12 12 @{article:Arcanist User Guide: Configuring a New Project}. 13 13 14 - = Overview = 14 + 15 + Overview 16 + ======== 15 17 16 18 "Lint" refers to a general class of programming tools which analyze source code 17 19 and raise warnings and errors about it. For example, a linter might raise ··· 35 37 languages. Arcanist ships with bindings for many popular tools, and you can 36 38 write new bindings fairly easily if you have custom tools. 37 39 38 - = Available Linters = 39 40 40 - Arcanist ships with bindings for these linters: 41 + Available Linters 42 + ================= 41 43 42 - - [[http://www.jshint.com/ | JSHint]], a Javascript linter. 43 - See @{class@arcanist:ArcanistJSHintLinter}. 44 - - [[http://pypi.python.org/pypi/pep8 | PEP8]], 45 - [[http://pypi.python.org/pypi/pyflakes | Pyflakes]], 46 - [[https://flake8.readthedocs.org/ | Flake8]], and 47 - [[http://pypi.python.org/pypi/pylint | Pylint]] - various Python linters. 48 - See @{class@arcanist:ArcanistPEP8Linter}, 49 - @{class@arcanist:ArcanistPyFlakesLinter}, 50 - @{class@arcanist:ArcanistFlake8Linter}, 51 - and @{class@arcanist:ArcanistPyLintLinter}. 52 - - [[http://pear.php.net/package/PHP_CodeSniffer | PHP CodeSniffer]], a 53 - PHP linter. See @{class@arcanist:ArcanistPhpcsLinter}. 54 - - [[http://cppcheck.sourceforge.net/ | Cppcheck]] and 55 - [[http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py | 56 - cpplint.py]], two checkers for C++. See 57 - @{class@arcanist:ArcanistCppcheckLinter} and 58 - @{class@arcanist:ArcanistCpplintLinter} respectively. 59 - - [[https://github.com/hach-que/cstools | cslint]], a C# linting tool based on 60 - [[http://stylecop.codeplex.com/ | StyleCop]]. See 61 - @{class@arcanist:ArcanistCSharpLinter}. 62 - - [[http://puppet-lint.com/ | puppet-lint]], a linter for 63 - [[http://puppetlabs.com/ | Puppet]] manifests. See 64 - @{class@arcanist:ArcanistPuppetLintLinter}. 65 - - [[http://www.ruby-lang.org | Ruby]]'s built-in checker. See 66 - @{class@arcanist:ArcanistRubyLinter}. 67 - - [[http://www.scala-sbt.org/ | sbt]], a built tool for Scala. See 68 - @{class@arcanist:ArcanistScalaSBTLinter}. 69 - - [[http://csslint.net/ | CSSLint]], for CSS: 70 - @{class@arcanist:ArcanistCSSLintLinter}. 71 - - [[https://github.com/less/less.js | lessc]], error detection in 72 - [[http://lesscss.org/ | LESS]] code. @{class@arcanist:ArcanistLesscLinter}. 73 - - [[http://php.net/simplexml | SimpleXML]] for XML files. 74 - @{class@arcanist:ArcanistXMLLinter}. 44 + To see a list of available linters, run: 75 45 46 + $ arc linters 76 47 77 - Arcanist also ships with generic bindings which can be configured to parse the 78 - output of a broad range of lint programs: 48 + Arcanist ships with bindings for a number of linters that can check for errors 49 + or problems in JS, CSS, PHP, Python, C, C++, C#, Less, Puppet, Ruby, JSON, XML, 50 + and several other languages. 79 51 80 - - @{class@arcanist:ArcanistScriptAndRegexLinter}, which runs a script and 81 - parses its output with a regular expression. 82 - - @{class@arcanist:ArcanistConduitLinter}, which invokes a linter over 83 - Conduit and can allow you to build client/server linters. 52 + Some general purpose linters are also available. These linters can check for 53 + cross-language issues like sensible filenames, trailing or mixed whitespace, 54 + character sets, spelling mistakes, and unresolved merge conflicts. 84 55 85 - Additionally, Arcanist ships with some general purpose linters: 56 + If you have a tool you'd like to use as a linter that isn't supported by 57 + default, you can write bindings for it. For information on writing new linter 58 + bindings, see @{article:Arcanist User Guide: Customizing Lint, Unit Tests and 59 + Workflows}. 86 60 87 - - @{class@arcanist:ArcanistTextLinter}, which enforces basic things like 88 - trailing whitespace, DOS newlines, file encoding, line widths, terminal 89 - newlines, and tab literals. 90 - - @{class@arcanist:ArcanistSpellingLinter}, which can detect common spelling 91 - mistakes. 92 - - @{class@arcanist:ArcanistFilenameLinter}, which can enforce generally 93 - sensible rules about not giving files nonsense names. 94 - - @{class@arcanist:ArcanistLicenseLinter}, which can make sure license 95 - headers are applied to all source files. 96 - - @{class@arcanist:ArcanistNoLintLinter}, which can disable lint for files 97 - marked unlintable. 98 - - @{class@arcanist:ArcanistGeneratedLinter}, which can disable lint for 99 - generated files. 100 - - @{class@arcanist:ArcanistMergeConflictLinter}, which detects unresolved 101 - merge conflicts. 102 61 103 - Finally, Arcanist has special-purpose linters: 62 + Configuring Lint 63 + ================ 104 64 105 - - @{class@arcanist:ArcanistXHPASTLinter}, the PHP linter used by Phabricator 106 - itself. This linter is powerful, but somewhat rigid (it enforces phutil 107 - rules and isn't very configurable for other rulesets). 108 - - @{class@arcanist:ArcanistPhutilLibraryLinter}, which enforces phutil library 109 - layout rules. 65 + To configure lint integration for your project, create a file called `.arclint` 66 + at the project root. This file should be in JSON format, and look like this: 110 67 111 - You can add support for new linters in three ways: 68 + ```lang=js 69 + { 70 + "linters": { 71 + "sample": { 72 + "type": "pep8" 73 + } 74 + } 75 + } 76 + ``` 112 77 113 - - write new bindings and contribute them to the upstream; 114 - - write new bindings and install them alongside Arcanist; or 115 - - use a generic binding like @{class@arcanist:ArcanistScriptAndRegexLinter} 116 - and drive the integration through configuration. 78 + Here, the key ("sample") is a human-readable label identifying the linter. It 79 + does not affect linter behavior, so just choose something that makes sense to 80 + you. 81 + 82 + The `type` specifies which linter to run. Use `arc linters` to find the names of 83 + the available linters. 117 84 118 - = Using Lint to Improve Code Review = 85 + **Including and Excluding Files**: By default, a linter will run on every file. 86 + This is appropriate for some linters (like the Filename linter), but normally 87 + you only want to run a linter like **pep8** on Python files. To include or 88 + exclude files, use `include` and `exclude`: 89 + 90 + ```lang=js 91 + { 92 + "linters": { 93 + "sample": { 94 + "type": "pep8", 95 + "include": "(\\.py$)", 96 + "exclude": "(^third-party/)" 97 + } 98 + } 99 + } 100 + ``` 101 + 102 + The `include` key is a regular expression (or list of regular expressions) 103 + identifying paths the linter should be run on, while `exclude` is a regular 104 + expression (or list of regular expressions) identifying paths which it should 105 + not run on. 106 + 107 + Thus, this configures a **pep8** linter named "sample" which will run on files 108 + ending in ".py", unless they are inside the "third-party/" directory. 109 + 110 + In these examples, regular expressions are written in this style: 111 + 112 + "(example/path)" 113 + 114 + They can be specified with any delimiters, but using `(` and `)` means you don't 115 + have to escape slashes in the expression, so it may be more convenient to 116 + specify them like this. If you prefer, these are all equivalent: 117 + 118 + "(example/path)i" 119 + "/example\\/path/i" 120 + "@example/path@i" 121 + 122 + You can also exclude files globally, so no linters run on them at all. Do this 123 + by specifying `exclude` at top level: 124 + 125 + ```lang=js 126 + { 127 + "exclude": "(^tests/data/)", 128 + "linters": { 129 + "sample": { 130 + "type": "pep8", 131 + "include": "(\\.py$)", 132 + "exclude": "(^third-party/)" 133 + } 134 + } 135 + } 136 + ``` 137 + 138 + Here, the addition of a global `exclude` rule means no linter will be run on 139 + files in "tests/data/". 140 + 141 + **Running Multiple Linters**: Often, you will want to run several different 142 + linters. Perhaps your project has a mixture of Python and Javascript code, or 143 + you have some PHP and some JSON files. To run multiple linters, just list 144 + them in the `linters` map: 145 + 146 + ```lang=js 147 + { 148 + "linters": { 149 + "jshint": { 150 + "type": "jshint", 151 + "include": "(\\.js$)" 152 + }, 153 + "xml": { 154 + "type": "xml", 155 + "include": "(\\.xml$)" 156 + } 157 + } 158 + } 159 + ``` 160 + 161 + This will run JSHint on `.js` files, and SimpleXML on `.xml` files. 162 + 163 + **Adjusting Message Severities**: Arcanist raises lint messages at various 164 + severities. Each message has a different severity: for example, lint might 165 + find a syntax error and raise an `error` about it, and find trailing whitespace 166 + and raise a `warning` about it. 167 + 168 + Normally, you will be presented with lint messages as you are sending code for 169 + review. In that context, the severities behave like this: 170 + 171 + - `error` When a file contains lint errors, they are always reported. These 172 + are intended to be severe problems, like a syntax error. Unresoved lint 173 + errors require you to confirm that you want to continue. 174 + - `warning` When a file contains warnings, they are reported by default only 175 + if they appear on lines that you have changed. They are intended to be 176 + minor problems, like unconventional whitespace. Unresolved lint warnings 177 + require you to confirm that you want to continue. 178 + - `autofix` This level is like `warning`, but if the message includes patches 179 + they will be applied automatically without prompting. 180 + - `advice` Like warnings, these messages are only reported on changed lines. 181 + They are intended to be very minor issues which may merit a note, like a 182 + "TODO" comment. They do not require confirmation. 183 + - `disabled` This level suppresses messages. They are not displayed. You can 184 + use this to turn off a message if you don't care about the issue it 185 + detects. 186 + 187 + By default, Arcanist tries to select reasonable severities for each message. 188 + However, you may want to make a message more or less severe, or disable it 189 + entirely. 190 + 191 + For many linters, you can do this by providing a `severity` map: 192 + 193 + ```lang=js 194 + { 195 + "linters": { 196 + "sample": { 197 + "type": "pep8", 198 + "severity": { 199 + "E221": "disabled", 200 + "E401": "warning" 201 + } 202 + } 203 + } 204 + } 205 + ``` 206 + 207 + Here, the lint message "E221" (which is "multiple spaces before operator") is 208 + disabled, so it won't be shown. The message "E401" (which is "multiple imports 209 + on one line") is set to "warning" severity. 210 + 211 + If you want to remap a large number of messages, you can use `severity.rules` 212 + and specify regular expressions: 213 + 214 + ```lang=js 215 + { 216 + "linters": { 217 + "sample": { 218 + "type": "pep8", 219 + "severity.rules": { 220 + "(^E)": "warning", 221 + "(^W)": "advice" 222 + } 223 + } 224 + } 225 + } 226 + ``` 227 + 228 + This adjusts the severity of all "E" codes to "warning", and all "W" codes to 229 + "advice". 230 + 231 + **Locating Binaries and Interpreters**: Normally, Arcanist expects to find 232 + external linters (like `pep8`) in `$PATH`, and be able to run them without any 233 + special qualifiers. That is, it will run a command similar to: 234 + 235 + $ pep8 example.py 236 + 237 + If you want to use a different copy of a linter binary, or invoke it in an 238 + explicit way, you can use `interpreter` and `bin`. These accept strings (or 239 + lists of strings) identifying places to look for linters. For example: 240 + 241 + 242 + ```lang=js 243 + { 244 + "linters": { 245 + "sample": { 246 + "type": "pep8", 247 + "interpreter": ["python2.6", "python"], 248 + "bin": ["/usr/local/bin/pep8-1.5.6", "/usr/local/bin/pep8"] 249 + } 250 + } 251 + } 252 + ``` 253 + 254 + When configured like this, `arc` will walk the `interpreter` list to find an 255 + available interpreter, then walk the `bin` list to find an available binary. 256 + If it can locate an appropriate interpreter and binary, it will execute those 257 + instead of the defaults. For example, this might cause it to execute a command 258 + similar to: 259 + 260 + $ python2.6 /usr/local/bin/pep8-1.5.6 example.py 261 + 262 + **Additional Options**: Some linters support additional options to configure 263 + their behavior. You can run this command get a list of these options and 264 + descriptions of what they do and how to configure them: 265 + 266 + $ arc linters --verbose 267 + 268 + This will show the available options for each linter in detail. 269 + 270 + **Running Different Rules on Different Files**: Sometimes, you may want to 271 + run the same linter with different rulesets on different files. To do this, 272 + create two copies of the linter and just give them different keys in the 273 + `linters` map: 274 + 275 + ```lang=js 276 + { 277 + "linters": { 278 + "pep8-relaxed": { 279 + "type": "pep8", 280 + "include": "(^legacy/.*\\.py$)", 281 + "severity.rules": { 282 + "(.*)": "advice" 283 + } 284 + }, 285 + "pep8-normal": { 286 + "type": "pep8", 287 + "include": "(\\.py$)", 288 + "exclude": "(^legacy/)" 289 + } 290 + } 291 + } 292 + ``` 293 + 294 + This example will run a relaxed version of the linter (which raises every 295 + message as advice) on Python files in "legacy/", and a normal version everywhere 296 + else. 297 + 298 + **Example .arclint Files**: You can find a collection of example files in 299 + `arcanist/resources/arclint/` to use as a starting point or refer to while 300 + configuring your own `.arclint` file. 301 + 302 + Advanced Configuration: Lint Engines 303 + ==================================== 304 + 305 + If you need to specify how linters execute in greater detail than is possible 306 + with `.arclint`, you can write a lint engine in PHP to extend Arcanist. This is 307 + an uncommon, advanced use case. The remainder of this section overviews how the 308 + lint internals work, and discusses how to extend Arcanist with a custom lint 309 + engine. If your needs are met by `.arclint`, you can skip to the next section 310 + of this document. 311 + 312 + The lint pipeline has two major components: linters and lint engines. 313 + 314 + **Linters** are programs which detect problems in a source file. Usually a 315 + linter is an external script, which Arcanist runs and passes a path to, like 316 + `jshint` or `pep8`. 317 + 318 + The script emits some messages, and Arcanist parses the output into structured 319 + errors. A piece of glue code (like @{class@arcanist:ArcanistJSHintLinter} or 320 + @{class@arcanist:ArcanistPEP8Linter}) handles calling the external script and 321 + interpreting its output. 322 + 323 + **Lint engines** coordinate linters, and decide which linters should run on 324 + which files. For instance, you might want to run `jshint` on all your `.js` 325 + files, and `pep8.py` on all your `.py` files. And you might not want to lint 326 + anything in `externals/` or `third-party/`, and maybe there are other files 327 + which you want to exclude or apply special rules for. 328 + 329 + By default, Arcanist uses the 330 + @{class@arcanist:ArcanistConfigurationDrivenLintEngine} engine if there is 331 + an `.arclint` file present in the working copy. This engine reads the `.arclint` 332 + file and uses it to decide which linters should be run on which paths. If no 333 + `.arclint` is present, Arcanist does not select an engine by default. 334 + 335 + You can write a custom lint engine instead, which can make a more powerful set 336 + of decisions about which linters to run on which paths. For instructions on 337 + writing a custom lint engine, see @{article:Arcanist User Guide: Customizing 338 + Lint, Unit Tests and Workflows} and @{class@arcanist:ExampleLintEngine}. 339 + 340 + To name an alternate lint engine, set `lint.engine` in your `.arcconfig` to the 341 + name of a class which extends @{class@arcanist:ArcanistLintEngine}. For more 342 + information on `.arcconfig`, see @{article:Arcanist User Guide: Configuring a 343 + New Project}. 344 + 345 + You can also set a default lint engine by setting `lint.engine` in your global 346 + user config with `arc set-config lint.engine`, or specify one explicitly with 347 + `arc lint --engine <engine>`. This can be useful for testing. 348 + 349 + There are several other engines bundled with Arcanist, but they are primarily 350 + predate `.arclint` and are effectively obsolete. 351 + 352 + 353 + Using Lint to Improve Code Review 354 + ================================= 119 355 120 356 Code review is most valuable when it's about the big ideas in a change. It is 121 357 substantially less valuable when it devolves into nitpicking over style, ··· 140 376 discussion away from stylistic nitpicks and toward useful examination of large 141 377 ideas. 142 378 143 - It can also provide a straightforward solution to arguments about style: 379 + It can also provide a straightforward solution to arguments about style, if you 380 + adopt a policy like this: 144 381 145 382 - If a rule is important enough that it should be enforced, the proponent must 146 383 add it to lint so it is automatically detected or fixed in the future and ··· 151 388 This may or may not be an appropriate methodology to adopt at your organization, 152 389 but it generally puts the incentives in the right places. 153 390 154 - = Philosophy of Lint = 391 + 392 + Philosophy of Lint 393 + ================== 155 394 156 395 Some general thoughts on how to develop lint effectively, based on building 157 396 lint tools at Facebook: ··· 167 406 the problems they detect, but Arcanist supports this and it's quite 168 407 valuable to have the linter not only say "the convention is to put a space 169 408 after comma in a function call" but to fix it for you. 170 - 171 - 172 - = Configuring Lint = 173 - 174 - Most built in linters can be setup by creating a file named `.arclint` in the 175 - workspace root, which is read by 176 - @{class@arcanist:ArcanistConfigurationDrivenLintEngine}. 177 - 178 - It's a JSON file, which should look something like: 179 - { 180 - "linters" : { 181 - "sample" : { 182 - "type" : "pep8", 183 - "include" : "(\\.py$)", 184 - "exclude" : "(^exclude.py)", 185 - "severity" : { 186 - "E401" : "warning" 187 - } 188 - } 189 - } 190 - Where: 191 - - The key (`sample`) is only used for reporting, 192 - - `type` (`pep8`) is used to find the right linter, 193 - - `include` and `exclude` specify paths to run linter on, and 194 - - `severity` is specified by the linter implementation; Other linters may 195 - define other configurable values. 196 - 197 - You may specify many linters in a single `.arclint` file; For an example, see 198 - [[https://github.com/epriestley/arclint-examples/ | the arclint-examples 199 - repository]]. 200 - 201 - Currently available linter types are: `csharp`, `filename`, `csslint`, 202 - `flake8`, `jshint`, `jsonlint`, `lessc`, `pep8`, `phpcs`, `puppet-lint`, 203 - `pyflakes`, `ruby`, `generated`, `nolint`, `script-and-regex`, 204 - `spelling`, `text`, `xml`. 205 - 206 - = Legacy Setups: Using arcconfig = 207 - 208 - Integration with linters that do not (yet) fully support `.arclint` involves two 209 - major components: linters and lint engines. 210 - 211 - Linters themselves are programs which detect problems in a source file. Usually 212 - a linter is an external script, which Arcanist runs and passes a path to, like 213 - `jshint` or `pep8.py`. The script emits some messages, and Arcanist parses 214 - the output into structured errors. A piece of glue code (like 215 - @{class@arcanist:ArcanistJSHintLinter} or 216 - @{class@arcanist:ArcanistPEP8Linter}) handles calling the external script and 217 - interpreting its output. 218 - 219 - Lint engines coordinate linters, and decide which linters should run on which 220 - files. For instance, you might want to run `jshint` on all your `.js` files, 221 - and `pep8.py` on all your `.py` files. And you might not want to lint anything 222 - in `externals/` or `third-party/`, and maybe there are other files which you 223 - want to exclude or apply special rules for. 224 - 225 - To configure arc for lint, you specify the name of a lint engine, and possibly 226 - provide some additional configuration. To name a lint engine, set `lint.engine` 227 - in your `.arcconfig` to the name of a class which extends 228 - @{class@arcanist:ArcanistLintEngine}. For more information on `.arcconfig`, see 229 - @{article:Arcanist User Guide: Configuring a New Project}. 230 - 231 - You can also set a default lint engine by setting `lint.engine` in your global 232 - user config with `arc set-config lint.engine`, or specify one explicitly with 233 - `arc lint --engine <engine>`. 234 - 235 - The available engines are: 236 - 237 - - @{class@arcanist:ComprehensiveLintEngine}, which runs a wide array of 238 - linters on many types of files. This is probably of limited use in any real 239 - project because it is overbroad, but is a good starting point for getting 240 - lint doing things. 241 - - @{class@arcanist:ArcanistSingleLintEngine}, which runs a single linter on 242 - every file unconditionally. This can be used with a glue linter like 243 - @{class@arcanist:ArcanistScriptAndRegexLinter} to put engine logic in an 244 - external script. 245 - - A custom engine you write. For most projects, lint rules are sufficiently 246 - specialized that this is the best option. For instructions on writing a 247 - custom lint engine, see 248 - @{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows} 249 - and @{class@arcanist:ExampleLintEngine}. 250 - 251 409 252 410 = Next Steps = 253 411