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