Skip to content

Commit 8746b9e

Browse files
committed
Auto merge of #10598 - blyxyas:book-emit_lints, r=flip1995
Clippy Book Chapter Updates Reborn: Emitting lints The PR adds a new chapter to the book: "Emitting lints". This time it changed a lot from the old source file. ## Notes - For discussion about the whole project, please use the tracking issue for the project #10597 (It also contains a timeline, discussions, and more information) changelog: Add a new "Emitting lints" chapter to the book r? `@flip1995`
2 parents aa371eb + a26937f commit 8746b9e

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [Defining Lints](development/defining_lints.md)
1717
- [Writing tests](development/writing_tests.md)
1818
- [Lint Passes](development/lint_passes.md)
19+
- [Emitting lints](development/emitting_lints.md)
1920
- [Type Checking](development/type_checking.md)
2021
- [Method Checking](development/method_checking.md)
2122
- [Macro Expansions](development/macro_expansions.md)
+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Emitting a lint
2+
3+
Once we have [defined a lint](defining_lints.md), written [UI
4+
tests](writing_tests.md) and chosen [the lint pass](lint_passes.md) for the lint,
5+
we can begin the implementation of the lint logic so that we can emit it and
6+
gradually work towards a lint that behaves as expected.
7+
8+
Note that we will not go into concrete implementation of a lint logic in this
9+
chapter. We will go into details in later chapters as well as in two examples of
10+
real Clippy lints.
11+
12+
To emit a lint, we must implement a pass (see [Lint Passes](lint_passes.md)) for
13+
the lint that we have declared. In this example we'll implement a "late" lint,
14+
so take a look at the [LateLintPass][late_lint_pass] documentation, which
15+
provides an abundance of methods that we can implement for our lint.
16+
17+
```rust
18+
pub trait LateLintPass<'tcx>: LintPass {
19+
// Trait methods
20+
}
21+
```
22+
23+
By far the most common method used for Clippy lints is [`check_expr`
24+
method][late_check_expr], this is because Rust is an expression language and,
25+
more often than not, the lint we want to work on must examine expressions.
26+
27+
> _Note:_ If you don't fully understand what expressions are in Rust, take a
28+
> look at the official documentation on [expressions][rust_expressions]
29+
30+
Other common ones include the [`check_fn` method][late_check_fn] and the
31+
[`check_item` method][late_check_item].
32+
33+
### Emitting a lint
34+
35+
Inside the trait method that we implement, we can write down the lint logic and
36+
emit the lint with suggestions.
37+
38+
Clippy's [diagnostics] provides quite a few diagnostic functions that we can use
39+
to emit lints. Take a look at the documentation to pick one that suits your
40+
lint's needs the best. Some common ones you will encounter in the Clippy
41+
repository includes:
42+
43+
- [`span_lint`]: Emits a lint without providing any other information
44+
- [`span_lint_and_note`]: Emits a lint and adds a note
45+
- [`span_lint_and_help`]: Emits a lint and provides a helpful message
46+
- [`span_lint_and_sugg`]: Emits a lint and provides a suggestion to fix the code
47+
- [`span_lint_and_then`]: Like `span_lint`, but allows for a lot of output
48+
customization.
49+
50+
```rust
51+
impl<'tcx> LateLintPass<'tcx> for LintName {
52+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
53+
// Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
54+
if some_lint_expr_logic(expr) {
55+
span_lint_and_help(
56+
cx, // < The context
57+
LINT_NAME, // < The name of the lint in ALL CAPS
58+
expr.span, // < The span to lint
59+
"message on why the lint is emitted",
60+
None, // < An optional help span (to highlight something in the lint)
61+
"message that provides a helpful suggestion",
62+
);
63+
}
64+
}
65+
}
66+
```
67+
68+
> Note: The message should be matter of fact and avoid capitalization and
69+
> punctuation. If multiple sentences are needed, the messages should probably be
70+
> split up into an error + a help / note / suggestion message.
71+
72+
## Suggestions: Automatic fixes
73+
74+
Some lints know what to change in order to fix the code. For example, the lint
75+
[`range_plus_one`][range_plus_one] warns for ranges where the user wrote `x..y +
76+
1` instead of using an [inclusive range][inclusive_range] (`x..=y`). The fix to
77+
this code would be changing the `x..y + 1` expression to `x..=y`. **This is
78+
where suggestions come in**.
79+
80+
A suggestion is a change that the lint provides to fix the issue it is linting.
81+
The output looks something like this (from the example earlier):
82+
83+
```text
84+
error: an inclusive range would be more readable
85+
--> $DIR/range_plus_minus_one.rs:37:14
86+
|
87+
LL | for _ in 1..1 + 1 {}
88+
| ^^^^^^^^ help: use: `1..=1`
89+
```
90+
91+
**Not all suggestions are always right**, some of them require human
92+
supervision, that's why we have [Applicability][applicability].
93+
94+
Applicability indicates confidence in the correctness of the suggestion, some
95+
are always right (`Applicability::MachineApplicable`), but we use
96+
`Applicability::MaybeIncorrect` and others when talking about a suggestion that
97+
may be incorrect.
98+
99+
### Example
100+
101+
The same lint `LINT_NAME` but that emits a suggestion would look something like this:
102+
103+
```rust
104+
impl<'tcx> LateLintPass<'tcx> for LintName {
105+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
106+
// Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
107+
if some_lint_expr_logic(expr) {
108+
span_lint_and_sugg( // < Note this change
109+
cx,
110+
LINT_NAME,
111+
span,
112+
"message on why the lint is emitted",
113+
"use",
114+
format!("foo + {} * bar", snippet(cx, expr.span, "<default>")), // < Suggestion
115+
Applicability::MachineApplicable,
116+
);
117+
}
118+
}
119+
}
120+
```
121+
122+
Suggestions generally use the [`format!`][format_macro] macro to interpolate the
123+
old values with the new ones. To get code snippets, use one of the `snippet*`
124+
functions from `clippy_utils::source`.
125+
126+
## How to choose between notes, help messages and suggestions
127+
128+
Notes are presented separately from the main lint message, they provide useful
129+
information that the user needs to understand why the lint was activated. They
130+
are the most helpful when attached to a span.
131+
132+
Examples:
133+
134+
### Notes
135+
136+
```text
137+
error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
138+
--> $DIR/drop_forget_ref.rs:10:5
139+
|
140+
10 | forget(&SomeStruct);
141+
| ^^^^^^^^^^^^^^^^^^^
142+
|
143+
= note: `-D clippy::forget-ref` implied by `-D warnings`
144+
note: argument has type &SomeStruct
145+
--> $DIR/drop_forget_ref.rs:10:12
146+
|
147+
10 | forget(&SomeStruct);
148+
| ^^^^^^^^^^^
149+
```
150+
151+
### Help Messages
152+
153+
Help messages are specifically to help the user. These are used in situation
154+
where you can't provide a specific machine applicable suggestion. They can also
155+
be attached to a span.
156+
157+
Example:
158+
159+
```text
160+
error: constant division of 0.0 with 0.0 will always result in NaN
161+
--> $DIR/zero_div_zero.rs:6:25
162+
|
163+
6 | let other_f64_nan = 0.0f64 / 0.0;
164+
| ^^^^^^^^^^^^
165+
|
166+
= help: consider using `f64::NAN` if you would like a constant representing NaN
167+
```
168+
169+
### Suggestions
170+
171+
Suggestions are the most helpful, they are changes to the source code to fix the
172+
error. The magic in suggestions is that tools like `rustfix` can detect them and
173+
automatically fix your code.
174+
175+
Example:
176+
177+
```text
178+
error: This `.fold` can be more succinctly expressed as `.any`
179+
--> $DIR/methods.rs:390:13
180+
|
181+
390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
182+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
183+
|
184+
```
185+
186+
### Snippets
187+
188+
Snippets are pieces of the source code (as a string), they are extracted
189+
generally using the [`snippet`][snippet_fn] function.
190+
191+
For example, if you want to know how an item looks (and you know the item's
192+
span), you could use `snippet(cx, span, "..")`.
193+
194+
## Final: Run UI Tests to Emit the Lint
195+
196+
Now, if we run our [UI test](writing_tests.md), we should see that Clippy now
197+
produces output that contains the lint message we designed.
198+
199+
The next step is to implement the logic properly, which is a detail that we will
200+
cover in the next chapters.
201+
202+
[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html
203+
[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr
204+
[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn
205+
[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item
206+
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
207+
[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html
208+
[`span_lint`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint.html
209+
[`span_lint_and_note`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_note.html
210+
[`span_lint_and_help`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html
211+
[`span_lint_and_sugg`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
212+
[`span_lint_and_then`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_then.html
213+
[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
214+
[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
215+
[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html
216+
[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html
217+
[format_macro]: https://doc.rust-lang.org/std/macro.format.html

0 commit comments

Comments
 (0)