@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.

at recaptime-dev/main 419 lines 16 kB view raw
1@title Arcanist User Guide: Lint 2@group userguide 3 4Guide to lint, linters, and linter configuration. 5 6This is a configuration guide that helps you set up advanced features. If you're 7just getting started, you don't need to look at this yet. Instead, start with 8the @{article:Arcanist User Guide}. 9 10This guide explains how lint works when configured in an `arc` project. If 11you haven't set up a project yet, do that first. For instructions, see 12@{article:Arcanist User Guide: Configuring a New Project}. 13 14 15Overview 16======== 17 18"Lint" refers to a general class of programming tools which analyze source code 19and raise warnings and errors about it. For example, a linter might raise 20warnings about syntax errors, uses of undeclared variables, calls to deprecated 21functions, spacing and formatting conventions, misuse of scope, implicit 22fallthrough in switch statements, missing license headers, use of dangerous 23language features, or a variety of other issues. 24 25Integrating lint into your development pipeline has two major benefits: 26 27 - you can detect and prevent a large class of programming errors; and 28 - you can simplify code review by addressing many mechanical and formatting 29 problems automatically. 30 31When arc is integrated with a lint toolkit, it enables the `arc lint` command 32and runs lint on changes during `arc diff`. The user is prompted to fix errors 33and warnings before sending their code for review, and lint issues which are 34not fixed are visible during review. 35 36There are many lint and static analysis tools available for a wide variety of 37languages. Arcanist ships with bindings for many popular tools, and you can 38write new bindings fairly easily if you have custom tools. 39 40 41Available Linters 42================= 43 44To see a list of available linters, run: 45 46 $ arc linters 47 48Arcanist ships with bindings for a number of linters that can check for errors 49or problems in JS, CSS, PHP, Python, C, C++, C#, Less, Puppet, Ruby, JSON, XML, 50and several other languages. 51 52Some general purpose linters are also available. These linters can check for 53cross-language issues like sensible filenames, trailing or mixed whitespace, 54character sets, spelling mistakes, and unresolved merge conflicts. 55 56If you have a tool you'd like to use as a linter that isn't supported by 57default, you can write bindings for it. For information on writing new linter 58bindings, see @{article:Arcanist User Guide: Customizing Lint, Unit Tests and 59Workflows}. 60 61 62Configuring Lint 63================ 64 65To configure lint integration for your project, create a file called `.arclint` 66at the project root. This file should be in JSON format, and look like this: 67 68```lang=js 69{ 70 "linters": { 71 "sample": { 72 "type": "pep8" 73 } 74 } 75} 76``` 77 78Here, the key ("sample") is a human-readable label identifying the linter. It 79does not affect linter behavior, so just choose something that makes sense to 80you. 81 82The `type` specifies which linter to run. Use `arc linters` to find the names of 83the available linters. 84 85**Including and Excluding Files**: By default, a linter will run on every file. 86This is appropriate for some linters (like the Filename linter), but normally 87you only want to run a linter like **pep8** on Python files. To include or 88exclude 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 102The `include` key is a regular expression (or list of regular expressions) 103identifying paths the linter should be run on, while `exclude` is a regular 104expression (or list of regular expressions) identifying paths which it should 105not run on. 106 107Thus, this configures a **pep8** linter named "sample" which will run on files 108ending in ".py", unless they are inside the "third-party/" directory. 109 110In these examples, regular expressions are written in this style: 111 112 "(example/path)" 113 114They can be specified with any delimiters, but using `(` and `)` means you don't 115have to escape slashes in the expression, so it may be more convenient to 116specify them like this. If you prefer, these are all equivalent: 117 118 "(example/path)i" 119 "/example\\/path/i" 120 "@example/path@i" 121 122You can also exclude files globally, so no linters run on them at all. Do this 123by 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 138Here, the addition of a global `exclude` rule means no linter will be run on 139files in "tests/data/". 140 141**Running Multiple Linters**: Often, you will want to run several different 142linters. Perhaps your project has a mixture of Python and Javascript code, or 143you have some PHP and some JSON files. To run multiple linters, just list 144them 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 161This will run JSHint on `.js` files, and SimpleXML on `.xml` files. 162 163**Adjusting Message Severities**: Arcanist raises lint messages at various 164severities. Each message has a different severity: for example, lint might 165find a syntax error and raise an `error` about it, and find trailing whitespace 166and raise a `warning` about it. 167 168Normally, you will be presented with lint messages as you are sending code for 169review. 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. Unresolved 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 187By default, Arcanist tries to select reasonable severities for each message. 188However, you may want to make a message more or less severe, or disable it 189entirely. 190 191For 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 207Here, the lint message `E221` (which is "multiple spaces before operator") is 208disabled, so it won't be shown. The message `E401` (which is "multiple imports 209on one line") is set to "warning" severity. 210 211If you want to remap a large number of messages, you can use `severity.rules` 212and 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 228This 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 232external linters (like `pep8`) in `$PATH`, and be able to run them without any 233special qualifiers. That is, it will run a command similar to: 234 235 $ pep8 example.py 236 237If you want to use a different copy of a linter binary, or invoke it in an 238explicit way, you can use `interpreter` and `bin`. These accept strings (or 239lists 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 254When configured like this, `arc` will walk the `interpreter` list to find an 255available interpreter, then walk the `bin` list to find an available binary. 256If it can locate an appropriate interpreter and binary, it will execute those 257instead of the defaults. For example, this might cause it to execute a command 258similar 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 263their behavior. You can run this command get a list of these options and 264descriptions of what they do and how to configure them: 265 266 $ arc linters --verbose 267 268This will show the available options for each linter in detail. 269 270**Running Different Rules on Different Files**: Sometimes, you may want to 271run the same linter with different rulesets on different files. To do this, 272create 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 294This example will run a relaxed version of the linter (which raises every 295message as advice) on Python files in "legacy/", and a normal version everywhere 296else. 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 300configuring your own `.arclint` file. 301 302Advanced Configuration: Lint Engines 303==================================== 304 305If you need to specify how linters execute in greater detail than is possible 306with `.arclint`, you can write a lint engine in PHP to extend Arcanist. This is 307an uncommon, advanced use case. The remainder of this section overviews how the 308lint internals work, and discusses how to extend Arcanist with a custom lint 309engine. If your needs are met by `.arclint`, you can skip to the next section 310of this document. 311 312The lint pipeline has two major components: linters and lint engines. 313 314**Linters** are programs which detect problems in a source file. Usually a 315linter is an external script, which Arcanist runs and passes a path to, like 316`jshint` or `pep8`. 317 318The script emits some messages, and Arcanist parses the output into structured 319errors. A piece of glue code (like @{class@arcanist:ArcanistJSHintLinter} or 320@{class@arcanist:ArcanistPEP8Linter}) handles calling the external script and 321interpreting its output. 322 323**Lint engines** coordinate linters, and decide which linters should run on 324which files. For instance, you might want to run `jshint` on all your `.js` 325files, and `pep8.py` on all your `.py` files. And you might not want to lint 326anything in `externals/` or `third-party/`, and maybe there are other files 327which you want to exclude or apply special rules for. 328 329By default, Arcanist uses the 330@{class@arcanist:ArcanistConfigurationDrivenLintEngine} engine if there is 331an `.arclint` file present in the working copy. This engine reads the `.arclint` 332file 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 335You can write a custom lint engine instead, which can make a more powerful set 336of decisions about which linters to run on which paths. For instructions on 337writing a custom lint engine, see @{article:Arcanist User Guide: Customizing 338Lint, Unit Tests and Workflows}. 339 340To name an alternate lint engine, set `lint.engine` in your `.arcconfig` to the 341name of a class which extends @{class@arcanist:ArcanistLintEngine}. For more 342information on `.arcconfig`, see @{article:Arcanist User Guide: Configuring a 343New Project}. 344 345You can also set a default lint engine by setting `lint.engine` in your global 346user config with `arc set-config lint.engine`, or specify one explicitly with 347`arc lint --engine <engine>`. This can be useful for testing. 348 349There are several other engines bundled with Arcanist, but they are primarily 350predate `.arclint` and are effectively obsolete. 351 352 353Using Lint to Improve Code Review 354================================= 355 356Code review is most valuable when it's about the big ideas in a change. It is 357substantially less valuable when it devolves into nitpicking over style, 358formatting, and naming conventions. 359 360The best response to receiving a review request full of style problems is 361probably to reject it immediately, point the author at your coding convention 362documentation, and ask them to fix it before sending it for review. But even 363this is a pretty negative experience for both parties, and less experienced 364reviewers sometimes go through the whole review and point out every problem 365individually. 366 367Lint can greatly reduce the negativity of this whole experience (and the amount 368of time wasted arguing about these things) by enforcing style and formatting 369rules automatically. Arcanist supports linters that not only raise warnings 370about these problems, but provide patches and fix the problems for the author -- 371before the code goes to review. 372 373Good linter integration means that code is pretty much mechanically correct by 374the time any reviewer sees it, provides clear rules about style which are 375especially helpful to new authors, and has the overall effect of pushing 376discussion away from stylistic nitpicks and toward useful examination of large 377ideas. 378 379It can also provide a straightforward solution to arguments about style, if you 380adopt a policy like this: 381 382 - If a rule is important enough that it should be enforced, the proponent must 383 add it to lint so it is automatically detected or fixed in the future and 384 no one has to argue about it ever again. 385 - If it's not important enough for them to do the legwork to add it to lint, 386 they have to stop complaining about it. 387 388This may or may not be an appropriate methodology to adopt at your organization, 389but it generally puts the incentives in the right places. 390 391 392Philosophy of Lint 393================== 394 395Some general thoughts on how to develop lint effectively, based on building 396lint tools at Facebook: 397 398 - Don't write regex-based linters to enforce language rules. Use a real parser 399 or AST-based tool. This is not a domain you can get right at any nontrivial 400 complexity with raw regexes. That is not a challenge. Just don't do this. 401 - False positives are pretty bad and should be avoided. You should aim to 402 implement only rules that have very few false positives, and provide ways to 403 mark false positives as OK. If running lint always raises 30 warnings about 404 irrelevant nonsense, it greatly devalues the tool. 405 - Move toward autocorrect rules. Most linters do not automatically correct 406 the problems they detect, but Arcanist supports this and it's quite 407 valuable to have the linter not only say "the convention is to put a space 408 after comma in a function call" but to fix it for you. 409 410= Next Steps = 411 412Continue by: 413 414 - integrating and customizing built-in linters and lint bindings with 415 @{article:Arcanist User Guide: Customizing Existing Linters}; or 416 - use a linter that hasn't been integrated into Arcanist with 417 @{article:Arcanist User Guide: Script and Regex Linter}; or 418 - learning how to add new linters and lint engines with 419 @{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}.