|
| 1 | +# Type Checking |
| 2 | + |
| 3 | +When we work on a new lint or improve an existing lint, we might want |
| 4 | +to retrieve the type `Ty` of an expression `Expr` for a variety of |
| 5 | +reasons. This can be achieved by utilizing the [`LateContext`][LateContext] |
| 6 | +that is available for [`LateLintPass`][LateLintPass]. |
| 7 | + |
| 8 | +## `LateContext` and `TypeckResults` |
| 9 | + |
| 10 | +The lint context [`LateContext`][LateContext] and [`TypeckResults`][TypeckResults] |
| 11 | +(returned by `LateContext::typeck_results`) are the two most useful data structures |
| 12 | +in `LateLintPass`. They allow us to jump to type definitions and other compilation |
| 13 | +stages such as HIR. |
| 14 | + |
| 15 | +> Note: `LateContext.typeck_results`'s return value is [`TypeckResults`][TypeckResults] |
| 16 | +> and is created in the type checking step, it includes useful information such as types of |
| 17 | +> expressions, ways to resolve methods and so on. |
| 18 | +
|
| 19 | +`TypeckResults` contains useful methods such as [`expr_ty`][expr_ty], |
| 20 | +which gives us access to the underlying structure [`Ty`][Ty] of a given expression. |
| 21 | + |
| 22 | +```rust |
| 23 | +pub fn expr_ty(&self, expr: &Expr<'_>) -> Ty<'tcx> |
| 24 | +``` |
| 25 | + |
| 26 | +As a side note, besides `expr_ty`, [`TypeckResults`][TypeckResults] contains a |
| 27 | +[`pat_ty()`][pat_ty] method that is useful for retrieving a type from a pattern. |
| 28 | + |
| 29 | +## `Ty` |
| 30 | + |
| 31 | +`Ty` struct contains the type information of an expression. |
| 32 | +Let's take a look at `rustc_middle`'s [`Ty`][Ty] struct to examine this struct: |
| 33 | + |
| 34 | +```rust |
| 35 | +pub struct Ty<'tcx>(Interned<'tcx, WithStableHash<TyS<'tcx>>>); |
| 36 | +``` |
| 37 | + |
| 38 | +At a first glance, this struct looks quite esoteric. But at a closer look, |
| 39 | +we will see that this struct contains many useful methods for type checking. |
| 40 | + |
| 41 | +For instance, [`is_char`][is_char] checks if the given `Ty` struct corresponds |
| 42 | +to the primitive character type. |
| 43 | + |
| 44 | +### `is_*` Usage |
| 45 | + |
| 46 | +In some scenarios, all we need to do is check if the `Ty` of an expression |
| 47 | +is a specific type, such as `char` type, so we could write the following: |
| 48 | + |
| 49 | +```rust |
| 50 | +impl LateLintPass<'_> for MyStructLint { |
| 51 | + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { |
| 52 | + // Get type of `expr` |
| 53 | + let ty = cx.typeck_results().expr_ty(expr); |
| 54 | + |
| 55 | + // Check if the `Ty` of this expression is of character type |
| 56 | + if ty.is_char() { |
| 57 | + println!("Our expression is a char!"); |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +Furthermore, if we examine the [source code][is_char_source] for `is_char`, |
| 64 | +we find something very interesting: |
| 65 | + |
| 66 | +```rust |
| 67 | +#[inline] |
| 68 | +pub fn is_char(self) -> bool { |
| 69 | + matches!(self.kind(), Char) |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +Indeed, we just discovered `Ty`'s [`kind` method][kind], which provides us |
| 74 | +with [`TyKind`][TyKind] of a `Ty`. |
| 75 | + |
| 76 | +## `TyKind` |
| 77 | + |
| 78 | +`TyKind` defines the kinds of types in Rust's type system. |
| 79 | +Peeking into [`TyKind` documentation][TyKind], we will see that it is an |
| 80 | +enum of 27 variants, including items such as `Bool`, `Int`, `Ref`, etc. |
| 81 | + |
| 82 | +### `kind` Usage |
| 83 | + |
| 84 | +The `TyKind` of `Ty` can be returned by calling [`Ty.kind` method][kind]. |
| 85 | +We often use this method to perform pattern matching in Clippy. |
| 86 | + |
| 87 | +For instance, if we want to check for a `struct`, we could examine if the |
| 88 | +`ty.kind` corresponds to an [`Adt`][Adt] (algebraic data type) and if its |
| 89 | +[`AdtDef`][AdtDef] is a struct: |
| 90 | + |
| 91 | +```rust |
| 92 | +impl LateLintPass<'_> for MyStructLint { |
| 93 | + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { |
| 94 | + // Get type of `expr` |
| 95 | + let ty = cx.typeck_results().expr_ty(expr); |
| 96 | + // Match its kind to enter the type |
| 97 | + match ty.kind { |
| 98 | + ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), |
| 99 | + _ => () |
| 100 | + } |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +## `hir::Ty` and `ty::Ty` |
| 106 | + |
| 107 | +We've been talking about [`ty::Ty`][middle_ty] this whole time without addressing [`hir::Ty`][hir_ty], but the latter |
| 108 | +is also important to understand. |
| 109 | + |
| 110 | +`hir::Ty` would represent *what* an user wrote, while `ty::Ty` would understand the meaning of it (because it has more |
| 111 | +information). |
| 112 | + |
| 113 | +**Example: `fn foo(x: u32) -> u32 { x }`** |
| 114 | + |
| 115 | +Here the HIR sees the types without "thinking" about them, it knows that the function takes an `u32` and returns |
| 116 | +an `u32`. But at the `ty::Ty` level the compiler understands that they're the same type, in-depth lifetimes, etc... |
| 117 | + |
| 118 | +you can use the [`hir_ty_to_ty`][hir_ty_to_ty] function to convert from a `hir::Ty` to a `ty::Ty` |
| 119 | + |
| 120 | +## Useful Links |
| 121 | + |
| 122 | +Below are some useful links to further explore the concepts covered |
| 123 | +in this chapter: |
| 124 | + |
| 125 | +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) |
| 126 | +- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) |
| 127 | +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) |
| 128 | +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) |
| 129 | + |
| 130 | +[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt |
| 131 | +[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html |
| 132 | +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty |
| 133 | +[is_char]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.is_char |
| 134 | +[is_char_source]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/sty.rs.html#1831-1834 |
| 135 | +[kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind |
| 136 | +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html |
| 137 | +[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html |
| 138 | +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty |
| 139 | +[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html |
| 140 | +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html |
| 141 | +[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html |
| 142 | +[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html |
| 143 | +[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html |
| 144 | +[hir_ty_to_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir_analysis/fn.hir_ty_to_ty.html |
0 commit comments