Skip to content

Commit 1c06375

Browse files
committed
Add new chapter: "Trait Checking"
1 parent 90cb0fa commit 1c06375

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [Adding Lints](development/adding_lints.md)
1616
- [Lint Passes](development/lint_passes.md)
1717
- [Type Checking](development/type_checking.md)
18+
- [Trait Checking](development/trait_checking.md)
1819
- [Macro Expansions](development/macro_expansions.md)
1920
- [Common Tools](development/common_tools_writing_lints.md)
2021
- [Infrastructure](development/infrastructure/README.md)
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Trait Checking
2+
3+
Besides [type checking](type_checking.md), we might want to examine if
4+
a specific type `Ty` implements certain trait when implementing a lint.
5+
There are three approaches to achieve this, depending on if the target trait
6+
that we want to examine has a [diagnostic item][diagnostic_items],
7+
[lang item][lang_items], or neither.
8+
9+
## Using Diagnostic Items
10+
11+
As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items
12+
are introduced for identifying types via [Symbols][symbol].
13+
14+
While the Rust Compiler Development Guide has [a section][using_diagnostic_items] on
15+
how to check for a specific trait on a type `Ty`, Clippy provides
16+
a helper function `is_trait_method`, which simplifies the process for us.
17+
18+
For instance, if we want to examine whether an expression implements
19+
the `Iterator` trait, we could simply write the following code,
20+
providing the `LateContext` (`cx`), our expression at hand, and
21+
the symbol of the trait in question:
22+
23+
```rust
24+
use clippy_utils::is_trait_method;
25+
use rustc_hir::Expr;
26+
use rustc_lint::{LateContext, LateLintPass};
27+
use rustc_span::symbol::sym;
28+
29+
impl LateLintPass<'_> for CheckIteratorTraitLint {
30+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
31+
if is_trait_method(cx, expr, sym::Iterator) {
32+
println!("This expression implements `Iterator` trait!");
33+
}
34+
}
35+
}
36+
```
37+
38+
> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s.
39+
40+
## Using Lang Items
41+
42+
Besides diagnostic items, we can also use [`lang_items`][lang_items].
43+
Take a look at the documentation and we find that `LanguageItems` contains
44+
all language items both from the current crate or its
45+
dependencies.
46+
47+
Using one of its `*_trait` method, we could obtain the [DefId] of any
48+
specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar
49+
to many Rustaceans.
50+
51+
For instance, if we want to examine whether an expression `expr` implements
52+
`Drop` trait, we could access `LanguageItems` via our `LateContext`'s
53+
[TyCtxt], which provides a `lang_items` method that will return the id of
54+
`Drop` trait to us. Then, by calling Clippy utils function `implements_trait`
55+
we can check that the `Ty` of the `expr` implements the trait:
56+
57+
```rust
58+
use clippy_utils::implements_trait;
59+
use rustc_hir::Expr;
60+
use rustc_lint::{LateContext, LateLintPass};
61+
62+
impl LateLintPass<'_> for CheckDropTraitLint {
63+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
64+
let ty = cx.typeck_results().expr_ty(expr);
65+
if cx.tcx.lang_items()
66+
.drop_trait()
67+
.map_or(false, |id| implements_trait(cx, ty, id, &[])) {
68+
println!("`expr` implements `Drop` trait!");
69+
}
70+
}
71+
}
72+
```
73+
74+
## Using Type Path
75+
76+
If neither diagnostic item or lang item is available, we can use
77+
[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
78+
implementation.
79+
80+
> **Note**: This approach should be avoided if possible.
81+
82+
Below, we check if the given `expr` implements `tokio`'s
83+
[`AsyncReadExt`][AsyncReadExt] trait:
84+
85+
```rust
86+
use clippy_utils::{match_trait_method, paths};
87+
use rustc_hir::Expr;
88+
use rustc_lint::{LateContext, LateLintPass};
89+
90+
impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
91+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
92+
if match_trait_method(cx, expr, &paths::TOKIO_IO_ASYNCREADEXT) {
93+
println!("`expr` implements `TOKIO_IO_ASYNCREADEXT` trait!");
94+
}
95+
}
96+
}
97+
```
98+
99+
> **Note**: Even though all the `clippy_utils` methods we have seen in this
100+
> chapter takes `expr` as a parameter, these methods are actually using
101+
> each expression's `HirId` under the hood.
102+
103+
[AsyncReadExt]: https://docs.rs/tokio/latest/tokio/io/trait.AsyncReadExt.html
104+
[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
105+
[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
106+
[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html
107+
[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs
108+
[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/
109+
[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
110+
[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html
111+
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
112+
[using_diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html#using-diagnostic-items

0 commit comments

Comments
 (0)