1- ## Adding a new lint
1+ # Adding a new lint
22
33You are probably here because you want to add a new lint to Clippy. If this is
44the first time you're contributing to Clippy, this document guides you through
@@ -25,14 +25,14 @@ because that's clearly a non-descriptive name.
2525 - [ PR Checklist] ( #pr-checklist )
2626 - [ Cheatsheet] ( #cheatsheet )
2727
28- ### Setup
28+ ## Setup
2929
3030When working on Clippy, you will need the current git master version of rustc,
3131which can change rapidly. Make sure you're working near rust-clippy's master,
3232and use the ` setup-toolchain.sh ` script to configure the appropriate toolchain
3333for the Clippy directory.
3434
35- ### Getting Started
35+ ## Getting Started
3636
3737There is a bit of boilerplate code that needs to be set up when creating a new
3838lint. 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`,
4545as well as run ` cargo dev update_lints ` to register the new lint. Next, we'll
4646open up these files and add our lint!
4747
48- ### Testing
48+ ## Testing
4949
5050Let's write some tests first that we can execute while we iterate on our lint.
5151
@@ -88,12 +88,10 @@ fn main() {
8888 let a = A ;
8989 a . foo ();
9090}
91-
9291```
9392
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.
9795
9896While we are working on implementing our lint, we can keep running the UI
9997test. 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`.
105103Running ` TESTNAME=foo_functions cargo uitest ` should pass then. When we commit
106104our lint, we need to commit the generated ` .stderr ` files, too.
107105
108- ### Rustfix tests
106+ ## Rustfix tests
109107
110108If the lint you are working on is making use of structured suggestions, the
111109test 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.
116114Use ` tests/ui/update-all-references.sh ` to automatically generate the
117115` .fixed ` file after running the tests.
118116
119- ### Edition 2018 tests
117+ ## Edition 2018 tests
120118
121119Some features require the 2018 edition to work (e.g. ` async_await ` ), but
122120compile-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) .
124122
125- ### Testing manually
123+ ## Testing manually
126124
127125Manually testing against an example file can be useful if you have added some
128126` 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.
131129
132130With tests in place, let's have a look at implementing our lint now.
133131
134- ### Lint declaration
132+ ## Lint declaration
135133
136134Let's start by opening the new file created in the ` clippy_lints ` crate
137135at ` 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:
140138``` rust
141139use rustc_lint :: {EarlyLintPass , EarlyContext };
142140use rustc_session :: {declare_lint_pass, declare_tool_lint};
143- use syntax :: ast :: * ;
141+ use rustc_ast :: ast :: * ;
144142```
145143
146144The next step is to update the lint declaration. Lints are declared using the
@@ -167,8 +165,8 @@ declare_clippy_lint! {
167165```
168166
169167* 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 ] .
172170* ` FOO_FUNCTIONS ` is the name of our lint. Be sure to follow the [ lint naming
173171 guidelines] [ lint_naming ] here when naming your lint. In short, the name should
174172 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
199197` register_plugins ` function in ` clippy_lints/src/lib.rs ` :
200198
201199``` rust
202- store . register_early_pass (box foo_functions :: FooFunctions );
200+ store . register_early_pass (|| box foo_functions :: FooFunctions );
203201```
204202
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
208204
209- ### Lint passes
205+ ## Lint passes
210206
211207Writing a lint that only checks for the name of a function means that we only
212208have 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
224220` --pass=early ` when running the new lint automation and all the imports were
225221added accordingly.
226222
227- ### Emitting a lint
223+ ## Emitting a lint
228224
229225With UI tests and the lint declaration in place, we can start working on the
230226implementation of the lint logic.
@@ -233,7 +229,7 @@ Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
233229
234230``` rust
235231impl 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 ) {
237233 // TODO: Emit lint here
238234 }
239235}
@@ -255,7 +251,7 @@ automatically. This is how it looks:
255251
256252``` rust
257253impl 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 ) {
259255 span_lint_and_help (
260256 cx ,
261257 FOO_FUNCTIONS ,
@@ -269,20 +265,20 @@ impl EarlyLintPass for FooFunctions {
269265
270266Running our UI test should now produce output that contains the lint message.
271267
272- ### Adding the lint logic
268+ ## Adding the lint logic
273269
274270Writing the logic for your lint will most likely be different from our example,
275271so this section is kept rather short.
276272
277273Using 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 ] .
280276
281277With that we can expand our ` check_fn ` method to:
282278
283279``` rust
284280impl 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 ) {
286282 if is_foo_fn (fn_kind ) {
287283 span_lint_and_help (
288284 cx ,
@@ -307,9 +303,11 @@ In our example, `is_foo_fn` looks like:
307303
308304fn is_foo_fn (fn_kind : FnKind <'_ >) -> bool {
309305 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
313311 FnKind :: Closure (.. ) => false
314312 }
315313}
@@ -325,13 +323,14 @@ implementation is not violating any Clippy lints itself.
325323That should be it for the lint implementation. Running ` cargo test ` should now
326324pass.
327325
328- ### Author lint
326+ ## Author lint
329327
330328If you have trouble implementing your lint, there is also the internal ` author `
331329lint to generate Clippy code that detects the offending pattern. It does not
332330work for all of the Rust syntax, but can give a good starting point.
333331
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 ] .
335334Put the code you want to lint into the editor and add the ` #[clippy::author] `
336335attribute above the item. Then run Clippy via ` Tools -> Clippy ` and you should
337336see the generated code in the output below.
@@ -341,7 +340,7 @@ see the generated code in the output below.
341340If the command was executed successfully, you can copy the code over to where
342341you are implementing your lint.
343342
344- ### Documentation
343+ ## Documentation
345344
346345The final thing before submitting our PR is to add some documentation to our
347346lint declaration.
@@ -374,7 +373,7 @@ declare_clippy_lint! {
374373Once your lint is merged, this documentation will show up in the [ lint
375374list] [ lint_list ] .
376375
377- ### Running rustfmt
376+ ## Running rustfmt
378377
379378[ Rustfmt] ( https://github.com/rust-lang/rustfmt ) is a tool for formatting Rust
380379code according to style guidelines. Your code has to be formatted by ` rustfmt `
@@ -389,13 +388,13 @@ rustup component add rustfmt --toolchain=nightly
389388Use ` cargo dev fmt ` to format the whole codebase. Make sure that ` rustfmt ` is
390389installed for the nightly toolchain.
391390
392- ### Debugging
391+ ## Debugging
393392
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! ` ]
395394macro anywhere in your code. Running the tests should then include the debug
396395output in the ` stdout ` part.
397396
398- ### PR Checklist
397+ ## PR Checklist
399398
400399Before submitting your PR make sure you followed all of the basic requirements:
401400
@@ -408,7 +407,7 @@ Before submitting your PR make sure you followed all of the basic requirements:
408407- [ ] Added lint documentation
409408- [ ] Run ` cargo dev fmt `
410409
411- ### Cheatsheet
410+ ## Cheatsheet
412411
413412Here are some pointers to things you are likely going to need for every lint:
414413
@@ -426,38 +425,38 @@ Here are some pointers to things you are likely going to need for every lint:
426425For ` EarlyLintPass ` lints:
427426
428427* [ ` EarlyLintPass ` ] [ early_lint_pass ]
429- * [ ` syntax ::ast` ] [ ast ]
428+ * [ ` rustc_ast ::ast` ] [ ast ]
430429
431430For ` LateLintPass ` lints:
432431
433432* [ ` LateLintPass ` ] [ late_lint_pass ]
434433* [ ` Ty::TyKind ` ] [ ty ]
435434
436-
437435While most of Clippy's lint utils are documented, most of rustc's internals lack
438436documentation currently. This is unfortunate, but in most cases you can probably
439437get 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.
441439
442440[ lint_list ] : https://rust-lang.github.io/rust-clippy/master/index.html
443441[ lint_naming ] : https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
444442[ category_level_mapping ] : https://github.com/rust-lang/rust-clippy/blob/bd23cb89ec0ea63403a17d3fc5e50c88e38dd54f/clippy_lints/src/lib.rs#L43
445443[ 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
450448[ diagnostics ] : https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
451449[ 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
453451[ span ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
454452[ applicability ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
455453[ if_chain ] : https://docs.rs/if_chain/*/if_chain/
456454[ 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
458456[ from_expansion ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
459457[ 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
462459[ rustc-dev-guide ] : https://rustc-dev-guide.rust-lang.org/
463460[ 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