]> git.lizzy.rs Git - rust.git/blob - doc/common_tools_writing_lints.md
Merge remote-tracking branch 'upstream/master' into rustup
[rust.git] / doc / common_tools_writing_lints.md
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 an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
8   - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
9   - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
10   - [Dealing with macros](#dealing-with-macros)
11
12 Useful Rustc dev guide links:
13 - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
14 - [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
15 - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
16 - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
17
18 # Retrieving the type of an expression
19
20 Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
21
22 - which type does this expression correspond to (using its [`TyKind`][TyKind])?
23 - is it a sized type?
24 - is it a primitive type?
25 - does it implement a trait?
26
27 This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
28 that gives you access to the underlying structure [`TyS`][TyS].
29
30 Example of use:
31 ```rust
32 impl LateLintPass<'_> for MyStructLint {
33     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
34         // Get type of `expr`
35         let ty = cx.typeck_results().expr_ty(expr);
36         // Match its kind to enter its type
37         match ty.kind {
38             ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
39             _ => ()
40         }
41     }
42 }
43 ```
44
45 Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method
46 to retrieve a type from a pattern.
47
48 Two noticeable items here:
49 - `cx` is the lint context [`LateContext`][LateContext]. The two most useful
50   data structures in this context are `tcx` and the `TypeckResults` returned by
51   `LateContext::typeck_results`, allowing us to jump to type definitions and
52   other compilation stages such as HIR.
53 - `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is
54   created by type checking step, it includes useful information such as types
55   of expressions, ways to resolve methods and so on.
56
57 # Checking if an expr is calling a specific method
58
59 Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
60
61 ```rust
62 impl LateLintPass<'_> for MyStructLint {
63     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
64         if_chain! {
65             // Check our expr is calling a method
66             if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
67             // Check the name of this method is `some_method`
68             if path.ident.name == sym!(some_method);
69             then {
70                 // ...
71             }
72         }
73     }
74 }
75 ```
76
77 # Checking if a type implements a specific trait
78
79 There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
80
81 ```rust
82 use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths};
83 use rustc_span::symbol::sym;
84
85 impl LateLintPass<'_> for MyStructLint {
86     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
87         // 1. Using diagnostic items with the expression
88         // we use `is_trait_method` function from Clippy's utils
89         if is_trait_method(cx, expr, sym::Iterator) {
90             // method call in `expr` belongs to `Iterator` trait
91         }
92
93         // 2. Using lang items with the expression type
94         let ty = cx.typeck_results().expr_ty(expr);
95         if cx.tcx.lang_items()
96             // we are looking for the `DefId` of `Drop` trait in lang items
97             .drop_trait()
98             // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
99             .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
100                 // `expr` implements `Drop` trait
101             }
102
103         // 3. Using the type path with the expression
104         // we use `match_trait_method` function from Clippy's utils
105         if match_trait_method(cx, expr, &paths::INTO) {
106             // `expr` implements `Into` trait
107         }
108     }
109 }
110 ```
111
112 > Prefer using diagnostic and lang items, if the target trait has one.
113
114 We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
115 A list of defined paths for Clippy can be found in [paths.rs][paths]
116
117 # Checking if a type defines a specific method
118
119 To check if our type defines a method called `some_method`:
120
121 ```rust
122 use clippy_utils::{is_type_diagnostic_item, return_ty};
123
124 impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
125     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
126         if_chain! {
127             // Check if item is a method/function
128             if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
129             // Check the method is named `some_method`
130             if impl_item.ident.name == sym!(some_method);
131             // We can also check it has a parameter `self`
132             if signature.decl.implicit_self.has_implicit_self();
133             // We can go further and even check if its return type is `String`
134             if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
135             then {
136                 // ...
137             }
138         }
139     }
140 }
141 ```
142
143 # Dealing with macros
144
145 There are several helpers in [`clippy_utils`][utils] to deal with macros:
146
147 - `in_macro()`: detect if the given span is expanded by a macro
148
149 You may want to use this for example to not start linting in any macro.
150
151 ```rust
152 macro_rules! foo {
153     ($param:expr) => {
154         match $param {
155             "bar" => println!("whatever"),
156             _ => ()
157         }
158     };
159 }
160
161 foo!("bar");
162
163 // if we lint the `match` of `foo` call and test its span
164 assert_eq!(in_macro(match_span), true);
165 ```
166
167 - `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
168
169 You may want to use it for example to not start linting in macros from other crates
170
171 ```rust
172 #[macro_use]
173 extern crate a_crate_with_macros;
174
175 // `foo` is defined in `a_crate_with_macros`
176 foo!("bar");
177
178 // if we lint the `match` of `foo` call and test its span
179 assert_eq!(in_external_macro(cx.sess(), match_span), true);
180 ```
181
182 - `differing_macro_contexts()`: returns true if the two given spans are not from the same context
183
184 ```rust
185 macro_rules! m {
186     ($a:expr, $b:expr) => {
187         if $a.is_some() {
188             $b;
189         }
190     }
191 }
192
193 let x: Option<u32> = Some(42);
194 m!(x, x.unwrap());
195
196 // These spans are not from the same context
197 // x.is_some() is from inside the macro
198 // x.unwrap() is from outside the macro
199 assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
200 ```
201
202 [TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
203 [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
204 [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
205 [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
206 [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
207 [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
208 [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
209 [paths]: ../clippy_utils/src/paths.rs
210 [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs