Skip to content

Commit acd484e

Browse files
committed
Auto merge of #5641 - ThibsG:DocCommonTools, r=flip1995
Add common lint tools doc This PR starts adding some documentation about linting tools. `Retrieving all methods of a type` is not covered at this time. fixes partially: #3843 changelog: none
2 parents 6de17b0 + 8270412 commit acd484e

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-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

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

0 commit comments

Comments
 (0)