Skip to content

Commit 7d8419f

Browse files
committed
New chapter: Emitting lints
1 parent 5d149c5 commit 7d8419f

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [Development](development/README.md)
1414
- [Basics](development/basics.md)
1515
- [Adding Lints](development/adding_lints.md)
16+
- [Emitting lints](development/emitting_lints.md)
1617
- [Common Tools](development/common_tools_writing_lints.md)
1718
- [Infrastructure](development/infrastructure/README.md)
1819
- [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Emitting a lint
2+
3+
Once we have [defined a lint](define_lints.md), written [UI tests](write_tests.md)
4+
and chosen [the lint pass](lint_passes.md) for the lint, we can begin the
5+
implementation of the lint logic so that we can emit it and gradually work
6+
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
10+
of real Clippy lints.
11+
12+
In this chapter, we'll see an example of an imaginary `LateLintPass` lint named `bar_expressions`, that checks for expresions named `bar`.
13+
14+
To emit a lint with `LateLintPass`, we must implement it for the lint that we have
15+
declared. Take a look at the [LateLintPass][late_lint_pass] documentation, which
16+
provides an abundance of methods that we can implement for our lint.
17+
18+
```rust
19+
pub trait LateLintPass<'tcx>: LintPass {
20+
// Trait methods
21+
}
22+
```
23+
24+
By far the most common method used for Clippy lints is [`check_expr` method][late_check_expr],
25+
this is likely because Rust is an expression language and, more often than not,
26+
the lint we want to work on must examine expressions.
27+
28+
> _Note:_ If you don't fully understand what expressions are in Rust,
29+
> take a look at the official documentation on [expressions][rust_expressions]
30+
31+
Other common ones include the [`check_fn` method][late_check_fn] and the
32+
[`check_item` method][late_check_item]. We choose to implement whichever trait
33+
method based on what we need for the lint at hand.
34+
35+
### Implement Trait Method
36+
37+
Assume that we have added and defined a `BarExpressions` lint, we could write
38+
down a skeleton for this lint as the following:
39+
40+
```rust
41+
impl<'tcx> LateLintPass<'tcx> for BarExpressions {
42+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {}
43+
}
44+
```
45+
46+
### Emitting a lint
47+
48+
Inside the trait method that we implement, we can write down the lint logic
49+
and the emit the lint with suggestions.
50+
51+
Clippy's [diagnostics] provides quite a few diagnostic functions that we can
52+
use to emit lints. Take a look at the documentation to pick one that suits
53+
your lint's needs the best. Some common ones you will encounter in the Clippy
54+
repository includes:
55+
56+
- [span_lint_and_help]: emits a lint and provides a helpful message
57+
- [span_lint_and_sugg]: emits a lint and provides a suggestion to fix the code
58+
59+
```rust
60+
impl<'tcx> LateLintPass<'tcx> for BarExpressions {
61+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
62+
// Imagine that `some_bar_expr_logic` checks for requirements for emitting the lint
63+
if some_bar_expr_logic(expr) {
64+
span_lint_and_help(
65+
cx, // < The context
66+
FOO_FUNCTIONS, // < The name of the lint in ALL CAPS
67+
expr.span, // < The span to lint
68+
"message on why the lint is emitted",
69+
None, // < An optional help span (to highlight something in the lint)
70+
"message that provides a helpful suggestion",
71+
);
72+
}
73+
}
74+
}
75+
```
76+
77+
> Note: According to [the rustc-dev-guide], the message should be matter of fact and avoid
78+
> capitalization and periods, unless multiple sentences are needed. When code or
79+
> an identifier must appear in a message or label, it should be surrounded with
80+
> single grave accents (\`).
81+
82+
## Suggestions: Automatic fixes
83+
84+
Some lints know what to change in order to fix the lint. A real world example, the lint [`range_plus`][range_plus_one] lints for ranges where the user wrote `x..y + 1` instead of using an [inclusive range][inclusive_range] (`x..=1`). The fix to this lint would be just changing your `x..y + 1` expression to `x..=y`, **this is where suggestions come in**.
85+
86+
A suggestion is a change that the lint provides to fix the issue it is linting.
87+
The output looks something like this (from the example earlier):
88+
89+
```text
90+
error: an inclusive range would be more readable
91+
--> $DIR/range_plus_minus_one.rs:37:14
92+
|
93+
LL | for _ in 1..1 + 1 {}
94+
| ^^^^^^^^ help: use: `1..=1`
95+
```
96+
97+
**Not all suggestions are always right**, some of them require human supervision, that's why we have [Applicability][applicability].
98+
99+
Applicability indicates confidence in the correctness of the suggestion, some are always right (`Applicability::MachineApplicable`), but we use `Applicability::MaybeIncorrect` and other when talking about a lint that may be incorrect.
100+
101+
---
102+
103+
The same lint (`foo_functions`) but that emits a suggestion would be something like this:
104+
105+
```rust
106+
impl<'tcx> LateLintPass<'tcx> for BarExpressions {
107+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
108+
// Imagine that `some_bar_expr_logic` checks for requirements for emitting the lint
109+
if some_bar_expr_logic(expr) {
110+
span_lint_and_sugg( // < Note this change
111+
cx,
112+
BAR_EXPRESSIONS,
113+
span,
114+
"message on why the lint is emitted",
115+
"use",
116+
"suggestion (don't forget to integrate things from the source, like variable names)", // < Suggestion
117+
Applicability::MachineApplicable
118+
);
119+
}
120+
}
121+
}
122+
```
123+
124+
Suggestions generally use the [`format!`](format_macro) macro to interpolate the old values with the new ones.
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 information that the user needs to understand why the lint was activated. They are the most helpful when attached to a span.
129+
130+
Example:
131+
132+
```text
133+
error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
134+
--> $DIR/drop_forget_ref.rs:10:5
135+
|
136+
10 | forget(&SomeStruct);
137+
| ^^^^^^^^^^^^^^^^^^^
138+
|
139+
= note: `-D clippy::forget-ref` implied by `-D warnings`
140+
note: argument has type &SomeStruct
141+
--> $DIR/drop_forget_ref.rs:10:12
142+
|
143+
10 | forget(&SomeStruct);
144+
| ^^^^^^^^^^^
145+
```
146+
147+
---
148+
149+
Help messages are specifically to help the user. These are used in situation where you can't provide a specific machine applicable suggestion. They can also be attached to a span.
150+
151+
Example:
152+
153+
```text
154+
error: constant division of 0.0 with 0.0 will always result in NaN
155+
--> $DIR/zero_div_zero.rs:6:25
156+
|
157+
6 | let other_f64_nan = 0.0f64 / 0.0;
158+
| ^^^^^^^^^^^^
159+
|
160+
= help: consider using `f64::NAN` if you would like a constant representing NaN
161+
```
162+
163+
---
164+
165+
Suggestions are the most helpful, they are direct changes to the source code in order to fix the error. The magic in suggestions is that tools like rustfix can detect them and automatically fix your code.
166+
167+
Example:
168+
169+
```text
170+
error: This `.fold` can be more succinctly expressed as `.any`
171+
--> $DIR/methods.rs:390:13
172+
|
173+
390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
174+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
175+
|
176+
= note: `-D fold-any` implied by `-D warnings`
177+
```
178+
179+
### Snippets
180+
181+
Snippets are pieces of the source code (as a string), they are extracted generally using the [`snippet`][snippet_fn] function.
182+
183+
For example, if you want to know how an item looks (and you know the item's span), you could use `snippet(cx, span, "..")`.
184+
185+
## Final: Run UI Tests to Emit the Lint
186+
187+
Now, if we run our [UI test](write_tests.md), we should see that the compiler now
188+
produce output that contains the lint message we designed.
189+
190+
The next step is to implement the logic properly, which is a detail that we will
191+
cover in the next chapters.
192+
193+
[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html
194+
[early_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
195+
[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
196+
[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr
197+
[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn
198+
[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item
199+
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
200+
[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html
201+
[span_lint_and_help]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html
202+
[span_lint_and_sugg]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
203+
[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
204+
[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
205+
[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
206+
[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html
207+
[format_macro]: https://doc.rust-lang.org/std/macro.format.html
208+
[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html

0 commit comments

Comments
 (0)