Skip to content

Commit c082443

Browse files
committed
Add common lint tools doc
1 parent ce86f90 commit c082443

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

doc/adding_lints.md

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ Here are some pointers to things you are likely going to need for every lint:
465465
* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
466466
* [`Span`][span]
467467
* [`Applicability`][applicability]
468+
* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
468469
* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
469470
* [The nightly rustc docs][nightly_docs] which has been linked to throughout
470471
this guide

doc/common_tools_writing_lints.md

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Common tools for writing lints
2+
3+
You may need following tooltips to catch up with common operations.
4+
5+
- [Common tools for writing lints](#common-tools-for-writing-lints)
6+
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
7+
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
8+
- [Dealing with macros](#dealing-with-macros)
9+
10+
Useful Rustc dev guide links:
11+
- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
12+
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
13+
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
14+
15+
# Retrieving the type of an expression
16+
17+
Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
18+
19+
- which type does this expression correspond to (using its [`TyKind`][TyKind])?
20+
- is it a sized type?
21+
- is it a primitive type?
22+
- does it implement a trait?
23+
24+
This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckTables`][TypeckTables] struct, that gives you access to the underlying structure [`TyS`][TyS].
25+
26+
Example of use:
27+
```rust
28+
impl LateLintPass<'_, '_> for MyStructLint {
29+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
30+
// Get type of `expr`
31+
let ty = cx.tables.expr_ty(expr);
32+
// Match its kind to enter its type
33+
match ty.kind {
34+
ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
35+
_ => ()
36+
}
37+
}
38+
}
39+
```
40+
41+
Similarly in [`TypeckTables`][TypeckTables] methods, you have the [`pat_ty()`][pat_ty] method to retrieve a type from a pattern.
42+
43+
Two noticeable items here:
44+
- `cx` is the lint context [`LateContext`][LateContext]. The two most useful data structures in this context are `tcx` and `tables`, allowing us to jump to type definitions and other compilation stages such as HIR.
45+
- `tables` is [`TypeckTables`][TypeckTables] and is created by type checking step, it includes useful information such as types of expressions, ways to resolve methods and so on.
46+
47+
# Checking if a type implements a specific trait
48+
49+
There are two ways to do this, depending if the target trait is part of lang items.
50+
51+
```rust
52+
use crate::utils::{implements_trait, match_trait_method, paths};
53+
54+
impl LateLintPass<'_, '_> for MyStructLint {
55+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
56+
// 1. Using expression and Clippy's convenient method
57+
// we use `match_trait_method` function from Clippy's toolbox
58+
if match_trait_method(cx, expr, &paths::INTO) {
59+
// `expr` implements `Into` trait
60+
}
61+
62+
// 2. Using type context `TyCtxt`
63+
let ty = cx.tables.expr_ty(expr);
64+
if cx.tcx.lang_items()
65+
// we are looking for the `DefId` of `Drop` trait in lang items
66+
.drop_trait()
67+
// then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
68+
.map_or(false, |id| implements_trait(cx, ty, id, &[])) {
69+
// `expr` implements `Drop` trait
70+
}
71+
}
72+
}
73+
```
74+
75+
> Prefer using lang items, if the target trait is available there.
76+
77+
A list of defined paths for Clippy can be found in [paths.rs][paths]
78+
79+
We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
80+
81+
# Dealing with macros
82+
83+
There are several helpers in Clippy's utils to deal with macros:
84+
85+
- `in_macro()`: detect if the given span is expanded by a macro
86+
87+
You may want to use this for example to not start linting in any macro.
88+
89+
```rust
90+
macro_rules! foo {
91+
($param:expr) => {
92+
match $param {
93+
"bar" => println!("whatever"),
94+
_ => ()
95+
}
96+
};
97+
}
98+
99+
foo!("bar");
100+
101+
// if we lint the `match` of `foo` call and test its span
102+
assert_eq!(in_macro(match_span), true);
103+
```
104+
105+
- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
106+
107+
You may want to use it for example to not start linting in macros from other crates
108+
109+
```rust
110+
#[macro_use]
111+
extern crate a_crate_with_macros;
112+
113+
// `foo` is defined in `a_crate_with_macros`
114+
foo!("bar");
115+
116+
// if we lint the `match` of `foo` call and test its span
117+
assert_eq!(in_external_macro(cx.sess(), match_span), true);
118+
```
119+
120+
- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
121+
122+
```rust
123+
macro_rules! m {
124+
($a:expr, $b:expr) => {
125+
if $a.is_some() {
126+
$b;
127+
}
128+
}
129+
}
130+
131+
let x: Option<u32> = Some(42);
132+
m!(x, x.unwrap());
133+
134+
// These spans are not from the same context
135+
// x.is_some() is from inside the macro
136+
// x.unwrap() is from outside the macro
137+
assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
138+
```
139+
140+
[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
141+
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
142+
[TypeckTables]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html
143+
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html#method.expr_ty
144+
[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
145+
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
146+
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckTables.html#method.pat_ty
147+
[paths]: ../clippy_lints/src/utils/paths.rs

0 commit comments

Comments
 (0)