1
- ## Adding a new lint
1
+ # Adding a new lint
2
2
3
3
You are probably here because you want to add a new lint to Clippy. If this is
4
4
the first time you're contributing to Clippy, this document guides you through
@@ -25,14 +25,14 @@ because that's clearly a non-descriptive name.
25
25
- [ PR Checklist] ( #pr-checklist )
26
26
- [ Cheatsheet] ( #cheatsheet )
27
27
28
- ### Setup
28
+ ## Setup
29
29
30
30
When working on Clippy, you will need the current git master version of rustc,
31
31
which can change rapidly. Make sure you're working near rust-clippy's master,
32
32
and use the ` setup-toolchain.sh ` script to configure the appropriate toolchain
33
33
for the Clippy directory.
34
34
35
- ### Getting Started
35
+ ## Getting Started
36
36
37
37
There is a bit of boilerplate code that needs to be set up when creating a new
38
38
lint. Fortunately, you can use the clippy dev tools to handle this for you. We
@@ -45,7 +45,7 @@ two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
45
45
as well as run ` cargo dev update_lints ` to register the new lint. Next, we'll
46
46
open up these files and add our lint!
47
47
48
- ### Testing
48
+ ## Testing
49
49
50
50
Let's write some tests first that we can execute while we iterate on our lint.
51
51
@@ -88,12 +88,10 @@ fn main() {
88
88
let a = A ;
89
89
a . foo ();
90
90
}
91
-
92
91
```
93
92
94
- Now we can run the test with ` TESTNAME=foo_functions cargo uitest ` .
95
- Currently this test will fail. If you go through the output you will see that we
96
- are told that ` clippy::foo_functions ` is an unknown lint, which is expected.
93
+ Now we can run the test with ` TESTNAME=foo_functions cargo uitest ` ,
94
+ currently this test is meaningless though.
97
95
98
96
While we are working on implementing our lint, we can keep running the UI
99
97
test. That allows us to check if the output is turning into what we want.
@@ -105,7 +103,7 @@ every time before running `tests/ui/update-all-references.sh`.
105
103
Running ` TESTNAME=foo_functions cargo uitest ` should pass then. When we commit
106
104
our lint, we need to commit the generated ` .stderr ` files, too.
107
105
108
- ### Rustfix tests
106
+ ## Rustfix tests
109
107
110
108
If the lint you are working on is making use of structured suggestions, the
111
109
test file should include a ` // run-rustfix ` comment at the top. This will
@@ -116,13 +114,13 @@ test file and compare that to the contents of a `.fixed` file.
116
114
Use ` tests/ui/update-all-references.sh ` to automatically generate the
117
115
` .fixed ` file after running the tests.
118
116
119
- ### Edition 2018 tests
117
+ ## Edition 2018 tests
120
118
121
119
Some features require the 2018 edition to work (e.g. ` async_await ` ), but
122
120
compile-test tests run on the 2015 edition by default. To change this behavior
123
- add ` // compile-flags: -- edition 2018 ` at the top of the test file.
121
+ add ` // edition: 2018 ` at the top of the test file (note that it's space-sensitive) .
124
122
125
- ### Testing manually
123
+ ## Testing manually
126
124
127
125
Manually testing against an example file can be useful if you have added some
128
126
` println! ` s and the test suite output becomes unreadable. To try Clippy with
@@ -131,7 +129,7 @@ clippy-driver -- -L ./target/debug input.rs` from the working copy root.
131
129
132
130
With tests in place, let's have a look at implementing our lint now.
133
131
134
- ### Lint declaration
132
+ ## Lint declaration
135
133
136
134
Let's start by opening the new file created in the ` clippy_lints ` crate
137
135
at ` clippy_lints/src/foo_functions.rs ` . That's the crate where all the
@@ -140,7 +138,7 @@ lint code is. This file has already imported some initial things we will need:
140
138
``` rust
141
139
use rustc_lint :: {EarlyLintPass , EarlyContext };
142
140
use rustc_session :: {declare_lint_pass, declare_tool_lint};
143
- use syntax :: ast :: * ;
141
+ use rustc_ast :: ast :: * ;
144
142
```
145
143
146
144
The next step is to update the lint declaration. Lints are declared using the
@@ -167,8 +165,8 @@ declare_clippy_lint! {
167
165
```
168
166
169
167
* The section of lines prefixed with ` /// ` constitutes the lint documentation
170
- section. This is the default documentation style and will be displayed at
171
- https://rust-lang.github.io/rust-clippy/master/index.html .
168
+ section. This is the default documentation style and will be displayed
169
+ [ like this ] [ example_lint_page ] .
172
170
* ` FOO_FUNCTIONS ` is the name of our lint. Be sure to follow the [ lint naming
173
171
guidelines] [ lint_naming ] here when naming your lint. In short, the name should
174
172
state the thing that is being checked for and read well when used with
@@ -199,14 +197,12 @@ automate everything. We will have to register our lint pass manually in the
199
197
` register_plugins ` function in ` clippy_lints/src/lib.rs ` :
200
198
201
199
``` rust
202
- store . register_early_pass (box foo_functions :: FooFunctions );
200
+ store . register_early_pass (|| box foo_functions :: FooFunctions );
203
201
```
204
202
205
- This should fix the ` unknown clippy lint: clippy::foo_functions ` error that we
206
- saw when we executed our tests the first time. The next decision we have to make
207
- is which lint pass our lint is going to need.
203
+ [ example lint page ] : https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
208
204
209
- ### Lint passes
205
+ ## Lint passes
210
206
211
207
Writing a lint that only checks for the name of a function means that we only
212
208
have to deal with the AST and don't have to deal with the type system at all.
@@ -224,7 +220,7 @@ Since we don't need type information for checking the function name, we used
224
220
` --pass=early ` when running the new lint automation and all the imports were
225
221
added accordingly.
226
222
227
- ### Emitting a lint
223
+ ## Emitting a lint
228
224
229
225
With UI tests and the lint declaration in place, we can start working on the
230
226
implementation of the lint logic.
@@ -233,7 +229,7 @@ Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
233
229
234
230
``` rust
235
231
impl EarlyLintPass for FooFunctions {
236
- fn check_fn (& mut self , cx : & EarlyContext <'_ >, fn_kind : FnKind <'_ >, _ : & FnDecl , span : Span , _ : NodeId ) {
232
+ fn check_fn (& mut self , cx : & EarlyContext <'_ >, fn_kind : FnKind <'_ >, span : Span , _ : NodeId ) {
237
233
// TODO: Emit lint here
238
234
}
239
235
}
@@ -255,7 +251,7 @@ automatically. This is how it looks:
255
251
256
252
``` rust
257
253
impl EarlyLintPass for FooFunctions {
258
- fn check_fn (& mut self , cx : & EarlyContext <'_ >, _ : FnKind <'_ >, _ : & FnDecl , span : Span , _ : NodeId ) {
254
+ fn check_fn (& mut self , cx : & EarlyContext <'_ >, fn_kind : FnKind <'_ >, span : Span , _ : NodeId ) {
259
255
span_lint_and_help (
260
256
cx ,
261
257
FOO_FUNCTIONS ,
@@ -269,20 +265,20 @@ impl EarlyLintPass for FooFunctions {
269
265
270
266
Running our UI test should now produce output that contains the lint message.
271
267
272
- ### Adding the lint logic
268
+ ## Adding the lint logic
273
269
274
270
Writing the logic for your lint will most likely be different from our example,
275
271
so this section is kept rather short.
276
272
277
273
Using the [ ` check_fn ` ] [ check_fn ] method gives us access to [ ` FnKind ` ] [ fn_kind ]
278
- that has two relevant variants for us ` FnKind::ItemFn ` and ` FnKind::Method ` .
279
- Both provide access to the name of the function/method via an [ ` Ident ` ] [ ident ] .
274
+ that has the ` FnKind::Fn ` variant. It provides access to the name of the
275
+ function/method via an [ ` Ident ` ] [ ident ] .
280
276
281
277
With that we can expand our ` check_fn ` method to:
282
278
283
279
``` rust
284
280
impl EarlyLintPass for FooFunctions {
285
- fn check_fn (& mut self , cx : & EarlyContext <'_ >, fn_kind : FnKind <'_ >, _ : & FnDecl , span : Span , _ : NodeId ) {
281
+ fn check_fn (& mut self , cx : & EarlyContext <'_ >, fn_kind : FnKind <'_ >, span : Span , _ : NodeId ) {
286
282
if is_foo_fn (fn_kind ) {
287
283
span_lint_and_help (
288
284
cx ,
@@ -307,9 +303,11 @@ In our example, `is_foo_fn` looks like:
307
303
308
304
fn is_foo_fn (fn_kind : FnKind <'_ >) -> bool {
309
305
match fn_kind {
310
- FnKind :: ItemFn (ident , .. ) | FnKind :: Method (ident , .. ) => {
311
- ident . name == " foo"
312
- },
306
+ FnKind :: Fn (_ , ident , .. ) => {
307
+ // check if `fn` name is `foo`
308
+ ident . name. as_str () == " foo"
309
+ }
310
+ // ignore closures
313
311
FnKind :: Closure (.. ) => false
314
312
}
315
313
}
@@ -325,13 +323,14 @@ implementation is not violating any Clippy lints itself.
325
323
That should be it for the lint implementation. Running ` cargo test ` should now
326
324
pass.
327
325
328
- ### Author lint
326
+ ## Author lint
329
327
330
328
If you have trouble implementing your lint, there is also the internal ` author `
331
329
lint to generate Clippy code that detects the offending pattern. It does not
332
330
work for all of the Rust syntax, but can give a good starting point.
333
331
334
- The quickest way to use it, is the [ Rust playground: play.rust-lang.org] [ Play ] .
332
+ The quickest way to use it, is the
333
+ [ Rust playground: play.rust-lang.org] [ author_example ] .
335
334
Put the code you want to lint into the editor and add the ` #[clippy::author] `
336
335
attribute above the item. Then run Clippy via ` Tools -> Clippy ` and you should
337
336
see the generated code in the output below.
@@ -341,7 +340,7 @@ see the generated code in the output below.
341
340
If the command was executed successfully, you can copy the code over to where
342
341
you are implementing your lint.
343
342
344
- ### Documentation
343
+ ## Documentation
345
344
346
345
The final thing before submitting our PR is to add some documentation to our
347
346
lint declaration.
@@ -374,7 +373,7 @@ declare_clippy_lint! {
374
373
Once your lint is merged, this documentation will show up in the [ lint
375
374
list] [ lint_list ] .
376
375
377
- ### Running rustfmt
376
+ ## Running rustfmt
378
377
379
378
[ Rustfmt] ( https://github.com/rust-lang/rustfmt ) is a tool for formatting Rust
380
379
code according to style guidelines. Your code has to be formatted by ` rustfmt `
@@ -389,13 +388,13 @@ rustup component add rustfmt --toolchain=nightly
389
388
Use ` cargo dev fmt ` to format the whole codebase. Make sure that ` rustfmt ` is
390
389
installed for the nightly toolchain.
391
390
392
- ### Debugging
391
+ ## Debugging
393
392
394
- If you want to debug parts of your lint implementation, you can use the ` dbg! `
393
+ If you want to debug parts of your lint implementation, you can use the [ ` dbg! ` ]
395
394
macro anywhere in your code. Running the tests should then include the debug
396
395
output in the ` stdout ` part.
397
396
398
- ### PR Checklist
397
+ ## PR Checklist
399
398
400
399
Before submitting your PR make sure you followed all of the basic requirements:
401
400
@@ -408,7 +407,7 @@ Before submitting your PR make sure you followed all of the basic requirements:
408
407
- [ ] Added lint documentation
409
408
- [ ] Run ` cargo dev fmt `
410
409
411
- ### Cheatsheet
410
+ ## Cheatsheet
412
411
413
412
Here are some pointers to things you are likely going to need for every lint:
414
413
@@ -426,38 +425,38 @@ Here are some pointers to things you are likely going to need for every lint:
426
425
For ` EarlyLintPass ` lints:
427
426
428
427
* [ ` EarlyLintPass ` ] [ early_lint_pass ]
429
- * [ ` syntax ::ast` ] [ ast ]
428
+ * [ ` rustc_ast ::ast` ] [ ast ]
430
429
431
430
For ` LateLintPass ` lints:
432
431
433
432
* [ ` LateLintPass ` ] [ late_lint_pass ]
434
433
* [ ` Ty::TyKind ` ] [ ty ]
435
434
436
-
437
435
While most of Clippy's lint utils are documented, most of rustc's internals lack
438
436
documentation currently. This is unfortunate, but in most cases you can probably
439
437
get away with copying things from existing similar lints. If you are stuck,
440
- don't hesitate to ask on Discord, IRC or in the issue/PR.
438
+ don't hesitate to ask on [ Discord] or in the issue/PR.
441
439
442
440
[ lint_list ] : https://rust-lang.github.io/rust-clippy/master/index.html
443
441
[ lint_naming ] : https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
444
442
[ category_level_mapping ] : https://github.com/rust-lang/rust-clippy/blob/bd23cb89ec0ea63403a17d3fc5e50c88e38dd54f/clippy_lints/src/lib.rs#L43
445
443
[ declare_clippy_lint ] : https://github.com/rust-lang/rust-clippy/blob/a71acac1da7eaf667ab90a1d65d10e5cc4b80191/clippy_lints/src/lib.rs#L39
446
- [ check_fn ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint /trait.EarlyLintPass.html#method.check_fn
447
- [ early_lint_pass ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint /trait.EarlyLintPass.html
448
- [ late_lint_pass ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint /trait.LateLintPass.html
449
- [ fn_kind ] : https://doc.rust-lang.org/nightly/nightly-rustc/syntax /visit/enum.FnKind.html
444
+ [ check_fn ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint /trait.EarlyLintPass.html#method.check_fn
445
+ [ early_lint_pass ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint /trait.EarlyLintPass.html
446
+ [ late_lint_pass ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint /trait.LateLintPass.html
447
+ [ fn_kind ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast /visit/enum.FnKind.html
450
448
[ diagnostics ] : https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
451
449
[ utils ] : https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
452
- [ ident ] : https://doc.rust-lang.org/nightly/nightly-rustc/syntax/source_map /symbol/struct.Ident.html
450
+ [ ident ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span /symbol/struct.Ident.html
453
451
[ span ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
454
452
[ applicability ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
455
453
[ if_chain ] : https://docs.rs/if_chain/*/if_chain/
456
454
[ ty ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/sty/index.html
457
- [ ast ] : https://doc.rust-lang.org/nightly/nightly-rustc/syntax /ast/index.html
455
+ [ ast ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast /ast/index.html
458
456
[ from_expansion ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
459
457
[ in_external_macro ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/fn.in_external_macro.html
460
- [ play ] : https://play.rust-lang.org
461
- [ author_example ] : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f093b986e80ad62f3b67a1f24f5e66e2
458
+ [ author_example ] : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
462
459
[ rustc-dev-guide ] : https://rustc-dev-guide.rust-lang.org/
463
460
[ nightly_docs ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc/
461
+ [ Discord ] : https://discord.gg/rust-lang
462
+ [ `dbg`! ] : https://doc.rust-lang.org/std/macro.dbg.html
0 commit comments