@@ -11,21 +11,24 @@ because that's clearly a non-descriptive name.
11
11
- [ Setup] ( #setup )
12
12
- [ Getting Started] ( #getting-started )
13
13
- [ Testing] ( #testing )
14
+ - [ Cargo lints] ( #cargo-lints )
14
15
- [ Rustfix tests] ( #rustfix-tests )
15
16
- [ Edition 2018 tests] ( #edition-2018-tests )
16
17
- [ Testing manually] ( #testing-manually )
17
18
- [ Lint declaration] ( #lint-declaration )
19
+ - [ Lint registration] ( #lint-registration )
18
20
- [ Lint passes] ( #lint-passes )
19
21
- [ Emitting a lint] ( #emitting-a-lint )
20
22
- [ Adding the lint logic] ( #adding-the-lint-logic )
21
23
- [ Specifying the lint's minimum supported Rust version (MSRV)] ( #specifying-the-lints-minimum-supported-rust-version-msrv )
22
24
- [ Author lint] ( #author-lint )
25
+ - [ Print HIR lint] ( #print-hir-lint )
23
26
- [ Documentation] ( #documentation )
24
27
- [ Running rustfmt] ( #running-rustfmt )
25
28
- [ Debugging] ( #debugging )
26
29
- [ PR Checklist] ( #pr-checklist )
27
30
- [ Adding configuration to a lint] ( #adding-configuration-to-a-lint )
28
- - [ Cheatsheet ] ( #cheatsheet )
31
+ - [ Cheat Sheet ] ( #cheat-sheet )
29
32
30
33
## Setup
31
34
@@ -42,9 +45,9 @@ take a look at our [lint naming guidelines][lint_naming]. To get started on this
42
45
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
43
46
--category=pedantic` (category will default to nursery if not provided). This
44
47
command will create two files: ` tests/ui/foo_functions.rs ` and
45
- ` clippy_lints/src/foo_functions.rs ` , as well as run ` cargo dev update_lints ` to
46
- register the new lint. For cargo lints, two project hierarchies (fail/pass) will
47
- be created by default under ` tests/ui-cargo ` .
48
+ ` clippy_lints/src/foo_functions.rs ` , as well as
49
+ [ registering the lint] ( #lint-registration ) . For cargo lints, two project
50
+ hierarchies (fail/pass) will be created by default under ` tests/ui-cargo ` .
48
51
49
52
Next, we'll open up these files and add our lint!
50
53
@@ -155,7 +158,7 @@ Manually testing against an example file can be useful if you have added some
155
158
your local modifications, run
156
159
157
160
```
158
- env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
161
+ cargo dev lint input.rs
159
162
```
160
163
161
164
from the working copy root. With tests in place, let's have a look at
@@ -179,17 +182,15 @@ the auto-generated lint declaration to have a real description, something like t
179
182
180
183
``` rust
181
184
declare_clippy_lint! {
182
- /// ** What it does:**
185
+ /// ### What it does
183
186
///
184
- /// **Why is this bad?**
185
- ///
186
- /// **Known problems:** None.
187
- ///
188
- /// **Example:**
187
+ /// ### Why is this bad?
189
188
///
189
+ /// ### Example
190
190
/// ```rust
191
191
/// // example code
192
192
/// ```
193
+ #[clippy:: version = " 1.29.0" ]
193
194
pub FOO_FUNCTIONS ,
194
195
pedantic ,
195
196
" function named `foo`, which is not a descriptive name"
@@ -200,6 +201,10 @@ declare_clippy_lint! {
200
201
section. This is the default documentation style and will be displayed
201
202
[ like this] [ example_lint_page ] . To render and open this documentation locally
202
203
in a browser, run ` cargo dev serve ` .
204
+ * The ` #[clippy::version] ` attribute will be rendered as part of the lint documentation.
205
+ The value should be set to the current Rust version that the lint is developed in,
206
+ it can be retrieved by running ` rustc -vV ` in the rust-clippy directory. The version
207
+ is listed under * release* . (Use the version without the ` -nightly ` ) suffix.
203
208
* ` FOO_FUNCTIONS ` is the name of our lint. Be sure to follow the
204
209
[ lint naming guidelines] [ lint_naming ] here when naming your lint.
205
210
In short, the name should state the thing that is being checked for and
@@ -222,32 +227,34 @@ declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
222
227
impl EarlyLintPass for FooFunctions {}
223
228
```
224
229
225
- Normally after declaring the lint, we have to run ` cargo dev update_lints ` ,
226
- which updates some files, so Clippy knows about the new lint. Since we used
227
- ` cargo dev new_lint ... ` to generate the lint declaration, this was done
228
- automatically. While ` update_lints ` automates most of the things, it doesn't
229
- automate everything. We will have to register our lint pass manually in the
230
- ` register_plugins ` function in ` clippy_lints/src/lib.rs ` :
230
+ [ declare_clippy_lint ] : https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
231
+ [ example_lint_page ] : https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
232
+ [ lint_naming ] : https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
233
+ [ category_level_mapping ] : https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
234
+
235
+ ## Lint registration
236
+
237
+ When using ` cargo dev new_lint ` , the lint is automatically registered and
238
+ nothing more has to be done.
239
+
240
+ When declaring a new lint by hand and ` cargo dev update_lints ` is used, the lint
241
+ pass may have to be registered manually in the ` register_plugins ` function in
242
+ ` clippy_lints/src/lib.rs ` :
231
243
232
244
``` rust
233
- store . register_early_pass (|| box foo_functions :: FooFunctions );
245
+ store . register_early_pass (|| Box :: new ( foo_functions :: FooFunctions ) );
234
246
```
235
247
236
248
As one may expect, there is a corresponding ` register_late_pass ` method
237
249
available as well. Without a call to one of ` register_early_pass ` or
238
250
` register_late_pass ` , the lint pass in question will not be run.
239
251
240
- One reason that ` cargo dev ` does not automate this step is that multiple lints
241
- can use the same lint pass, so registering the lint pass may already be done
242
- when adding a new lint. Another reason that this step is not automated is that
243
- the order that the passes are registered determines the order the passes
244
- actually run, which in turn affects the order that any emitted lints are output
245
- in.
246
-
247
- [ declare_clippy_lint ] : https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
248
- [ example_lint_page ] : https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
249
- [ lint_naming ] : https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
250
- [ category_level_mapping ] : https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
252
+ One reason that ` cargo dev update_lints ` does not automate this step is that
253
+ multiple lints can use the same lint pass, so registering the lint pass may
254
+ already be done when adding a new lint. Another reason that this step is not
255
+ automated is that the order that the passes are registered determines the order
256
+ the passes actually run, which in turn affects the order that any emitted lints
257
+ are output in.
251
258
252
259
## Lint passes
253
260
@@ -425,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass
425
432
using the ` meets_msrv ` utility function.
426
433
427
434
``` rust
428
- if ! meets_msrv (self . msrv. as_ref (), & msrvs :: STR_STRIP_PREFIX ) {
435
+ if ! meets_msrv (self . msrv, msrvs :: STR_STRIP_PREFIX ) {
429
436
return ;
430
437
}
431
438
```
@@ -478,6 +485,19 @@ you are implementing your lint.
478
485
479
486
[ author_example ] : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
480
487
488
+ ## Print HIR lint
489
+
490
+ To implement a lint, it's helpful to first understand the internal representation
491
+ that rustc uses. Clippy has the ` #[clippy::dump] ` attribute that prints the
492
+ [ _ High-Level Intermediate Representation (HIR)_ ] of the item, statement, or
493
+ expression that the attribute is attached to. To attach the attribute to expressions
494
+ you often need to enable ` #![feature(stmt_expr_attributes)] ` .
495
+
496
+ [ Here] [ print_hir_example ] you can find an example, just select _ Tools_ and run _ Clippy_ .
497
+
498
+ [ _High-Level Intermediate Representation (HIR)_ ] : https://rustc-dev-guide.rust-lang.org/hir.html
499
+ [ print_hir_example ] : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
500
+
481
501
## Documentation
482
502
483
503
The final thing before submitting our PR is to add some documentation to our
@@ -487,21 +507,23 @@ Please document your lint with a doc comment akin to the following:
487
507
488
508
``` rust
489
509
declare_clippy_lint! {
490
- /// **What it does:** Checks for ... (describe what the lint matches).
510
+ /// ### What it does
511
+ /// Checks for ... (describe what the lint matches).
491
512
///
492
- /// **Why is this bad?** Supply the reason for linting the code.
513
+ /// ### Why is this bad?
514
+ /// Supply the reason for linting the code.
493
515
///
494
- /// **Known problems:** None. (Or describe where it could go wrong.)
495
- ///
496
- /// **Example:**
516
+ /// ### Example
497
517
///
498
518
/// ```rust,ignore
499
- /// // Bad
500
- /// Insert a short example of code that triggers the lint
501
- ///
502
- /// // Good
503
- /// Insert a short example of improved code that doesn't trigger the lint
519
+ /// // A short example of code that triggers the lint
504
520
/// ```
521
+ ///
522
+ /// Use instead:
523
+ /// ```rust,ignore
524
+ /// // A short example of improved code that doesn't trigger the lint
525
+ /// ```
526
+ #[clippy:: version = " 1.29.0" ]
505
527
pub FOO_FUNCTIONS ,
506
528
pedantic ,
507
529
" function named `foo`, which is not a descriptive name"
@@ -558,14 +580,16 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c
558
580
behavior that can be seen as a false positive for some users. Adding a configuration is done
559
581
in the following steps:
560
582
561
- 1 . Adding a new configuration entry to [ clippy_utils:: conf] ( /clippy_utils /src/conf.rs )
583
+ 1 . Adding a new configuration entry to [ clippy_lints::utils:: conf] ( /clippy_lints /src/utils /conf.rs )
562
584
like this:
563
585
``` rust
564
- /// Lint: LINT_NAME. <The configuration field doc comment>
586
+ /// Lint: LINT_NAME.
587
+ ///
588
+ /// <The configuration field doc comment>
565
589
(configuration_ident : Type = DefaultValue ),
566
590
```
567
- The configuration value and identifier should usually be the same . The doc comment will be
568
- automatically added to the lint documentation .
591
+ The doc comment is automatically added to the documentation of the listed lints . The default
592
+ value will be formatted using the ` Debug ` implementation of the type .
569
593
2 . Adding the configuration value to the lint impl struct :
570
594
1 . This first requires the definition of a lint impl struct . Lint impl structs are usually
571
595
generated with the `declare_lint_pass! ` macro . This struct needs to be defined manually
@@ -626,14 +650,14 @@ in the following steps:
626
650
with the configuration value and a rust file that should be linted by Clippy . The test can
627
651
otherwise be written as usual .
628
652
629
- ## Cheatsheet
653
+ ## Cheat Sheet
630
654
631
655
Here are some pointers to things you are likely going to need for every lint :
632
656
633
657
* [Clippy utils ][utils ] - Various helper functions . Maybe the function you need
634
- is already in here (` implements_trait `, ` match_def_path `, `snippet `, etc )
658
+ is already in here ([` is_type_diagnostic_item `], [` implements_trait `], [ `snippet `] , etc )
635
659
* [Clippy diagnostics ][diagnostics ]
636
- * [The ` if_chain ` macro ][ if_chain ]
660
+ * [Let chains ][ let - chains ]
637
661
* [`from_expansion `][from_expansion ] and [`in_external_macro `][in_external_macro ]
638
662
* [`Span `][span ]
639
663
* [`Applicability `][applicability ]
@@ -657,8 +681,11 @@ documentation currently. This is unfortunate, but in most cases you can probably
657
681
get away with copying things from existing similar lints . If you are stuck ,
658
682
don 't hesitate to ask on [Zulip ] or in the issue / PR .
659
683
660
- [utils ]: https : // github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
661
- [if_chain ]: https : // docs.rs/if_chain/*/if_chain/
684
+ [utils ]: https : // doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
685
+ [`is_type_diagnostic_item `]: https : // doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
686
+ [`implements_trait `]: https : // doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
687
+ [`snippet `]: https : // doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
688
+ [let - chains ]: https : // github.com/rust-lang/rust/pull/94927
662
689
[from_expansion ]: https : // doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
663
690
[in_external_macro ]: https : // doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
664
691
[span ]: https : // doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
0 commit comments