src/tools/clippy/book/src/development/trait_checking.md MARKDOWN 158 lines View on github.com → Search inside
1# Trait Checking23Besides [type checking](type_checking.md), we might want to examine if4a specific type `Ty` implements certain trait when implementing a lint.5There are three approaches to achieve this, depending on if the target trait6that we want to examine has a [diagnostic item][diagnostic_items],7[lang item][lang_items], or neither.89## Using Diagnostic Items1011As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items12are introduced for identifying types via [Symbols][symbol].1314For instance, if we want to examine whether an expression implements15the `Iterator` trait, we could simply write the following code,16providing the `LateContext` (`cx`), our expression at hand, and17the symbol of the trait in question:1819```rust20use clippy_utils::sym;21use clippy_utils::ty::implements_trait;22use rustc_hir::Expr;23use rustc_lint::{LateContext, LateLintPass};2425impl LateLintPass<'_> for CheckIteratorTraitLint {26    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {27        let implements_iterator = (cx.tcx.get_diagnostic_item(sym::Iterator))28            .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]));29        if implements_iterator {30            // [...]31        }3233    }34}35```3637> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s.3839## Using Lang Items4041Besides diagnostic items, we can also use [`lang_items`][lang_items].42Take a look at the documentation to find that `LanguageItems` contains43all language items defined in the compiler.4445Using one of its `*_trait` method, we could obtain the [DefId] of any46specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar47to many Rustaceans.4849For instance, if we want to examine whether an expression `expr` implements50`Drop` trait, we could access `LanguageItems` via our `LateContext`'s51[TyCtxt], which provides a `lang_items` method that will return the id of52`Drop` trait to us. Then, by calling Clippy utils function `implements_trait`53we can check that the `Ty` of the `expr` implements the trait:5455```rust56use clippy_utils::ty::implements_trait;57use rustc_hir::Expr;58use rustc_lint::{LateContext, LateLintPass};5960impl LateLintPass<'_> for CheckDropTraitLint {61    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {62        let ty = cx.typeck_results().expr_ty(expr);63        if cx.tcx.lang_items()64            .drop_trait()65            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {66                println!("`expr` implements `Drop` trait!");67            }68    }69}70```7172## Using Type Path7374If neither diagnostic item nor a language item is available, we can use75[`clippy_utils::paths`][paths] to determine get a trait's `DefId`.7677> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.7879Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html):8081```rust82use clippy_utils::paths;83use clippy_utils::ty::implements_trait;84use rustc_hir::Expr;85use rustc_lint::{LateContext, LateLintPass};8687impl LateLintPass<'_> for CheckIterStep {88    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {89        let ty = cx.typeck_results().expr_ty(expr);90        if let Some(trait_def_id) = paths::ITER_STEP.first(cx)91            && implements_trait(cx, ty, trait_def_id, &[])92        {93            println!("`expr` implements the `core::iter::Step` trait!");94        }95    }96}97```9899## Creating Types Programmatically100101Traits are often generic over a type parameter, e.g. `Borrow<T>` is generic102over `T`. Rust allows us to implement a trait for a specific type. For example,103we can implement `Borrow<[u8]>` for a hypothetical type `Foo`. Let's suppose104that we would like to find whether our type actually implements `Borrow<[u8]>`.105106To do so, we can use the same `implements_trait` function as above, and supply107a type parameter that represents `[u8]`. Since `[u8]` is a specialization of108`[T]`, we can use the  [`Ty::new_slice`][new_slice] method to create a type109that represents `[T]` and supply `u8` as a type parameter.110To create a `ty::Ty` programmatically, we rely on `Ty::new_*` methods. These111methods create a `TyKind` and then wrap it in a `Ty` struct. This means we112have access to all the primitive types, such as `Ty::new_char`,113`Ty::new_bool`, `Ty::new_int`, etc. We can also create more complex types,114such as slices, tuples, and references out of these basic building blocks.115116For trait checking, it is not enough to create the types, we need to convert117them into [GenericArg]. In rustc, a generic is an entity that the compiler118understands and has three kinds, type, const and lifetime. By calling119`.into()` on a constructed [Ty], we wrap the type into a generic which can120then be used by the query system to decide whether the specialized trait121is implemented.122123The following code demonstrates how to do this:124125```rust126127use rustc_middle::ty::Ty;128use clippy_utils::sym;129use clippy_utils::ty::implements_trait;130131let ty = todo!("Get the `Foo` type to check for a trait implementation");132let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code133let slice_of_bytes_t = Ty::new_slice(cx.tcx, cx.tcx.types.u8);134let generic_param = slice_of_bytes_t.into();135if implements_trait(cx, ty, borrow_id, &[generic_param]) {136    todo!("Rest of lint implementation")137}138```139140In essence, the [Ty] struct allows us to create types programmatically in a141representation that can be used by the compiler and the query engine. We then142use the `rustc_middle::Ty` of the type we are interested in, and query the143compiler to see if it indeed implements the trait we are interested in.144145146[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html147[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html148[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html149[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs150[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/151[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html152[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html153[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html154[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html155[rust]: https://github.com/rust-lang/rust156[new_slice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_slice157[GenericArg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.