Type Checking - Clippy Documentation

# Type Checking

When we work on a new lint or improve an existing lint, we might want to retrieve the type `Ty` of an expression `Expr` for a variety of reasons. This can be achieved by utilizing the `LateContext` that is available for `LateLintPass`.

## `LateContext` and `TypeckResults`

The lint context `LateContext` and `TypeckResults` (returned by `LateContext::typeck_results`) are the two most useful data structures in `LateLintPass`. They allow us to jump to type definitions and other compilation stages such as HIR.

> Note: `LateContext.typeck_results`'s return value is `TypeckResults` and is created in the type checking step, it includes useful information such as types of expressions, ways to resolve methods and so on.

`TypeckResults` contains useful methods such as `expr_ty`, which gives us access to the underlying structure `Ty` of a given expression.

    #![allow(unused)]
    fn main() {
    pub fn expr_ty(&self, expr: &Expr<'_>) -> Ty<'tcx>
    }

As a side note, besides `expr_ty`, `TypeckResults` contains a `pat_ty()` method that is useful for retrieving a type from a pattern.

## `Ty`

`Ty` struct contains the type information of an expression. Let's take a look at `rustc_middle`'s `Ty` struct to examine this struct:

    #![allow(unused)]
    fn main() {
    pub struct Ty<'tcx>(Interned<'tcx, WithStableHash<TyS<'tcx>>>);
    }

At a first glance, this struct looks quite esoteric. But at a closer look, we will see that this struct contains many useful methods for type checking.

For instance, `is_char` checks if the given `Ty` struct corresponds to the primitive character type.

### `is_*` Usage

In some scenarios, all we need to do is check if the `Ty` of an expression is a specific type, such as `char` type, so we could write the following:

    #![allow(unused)]
    fn main() {
    impl LateLintPass<'_> for MyStructLint {
        fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
            // Get type of `expr`
            let ty = cx.typeck_results().expr_ty(expr);

            // Check if the `Ty` of this expression is of character type
            if ty.is_char() {
                println!("Our expression is a char!");
            }
        }
    }
    }

Furthermore, if we examine the source code for `is_char`, we find something very interesting:

    #![allow(unused)]
    fn main() {
    #[inline]
    pub fn is_char(self) -> bool {
        matches!(self.kind(), Char)
    }
    }

Indeed, we just discovered `Ty`'s `kind()` method, which provides us with `TyKind` of a `Ty`.

## `TyKind`

`TyKind` defines the kinds of types in Rust's type system. Peeking into `TyKind` documentation, we will see that it is an enum of over 25 variants, including items such as `Bool`, `Int`, `Ref`, etc.

### `kind` Usage

The `TyKind` of `Ty` can be returned by calling `Ty.kind()` method. We often use this method to perform pattern matching in Clippy.

For instance, if we want to check for a `struct`, we could examine if the `ty.kind` corresponds to an `Adt` (algebraic data type) and if its `AdtDef` is a struct:

    #![allow(unused)]
    fn main() {
    impl LateLintPass<'_> for MyStructLint {
        fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
            // Get type of `expr`
            let ty = cx.typeck_results().expr_ty(expr);
            // Match its kind to enter the type
            match ty.kind() {
                ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
                _ => ()
            }
        }
    }
    }

## `hir::Ty` and `ty::Ty`

We've been talking about `ty::Ty` this whole time without addressing `hir::Ty`, but the latter is also important to understand.

`hir::Ty` would represent what the user wrote, while `ty::Ty` is how the compiler sees the type and has more information. Example:

    #![allow(unused)]
    fn main() {
    fn foo(x: u32) -> u32 { x }
    }

Here the HIR sees the types without "thinking" about them, it knows that the function takes an `u32` and returns an `u32`. As far as `hir::Ty` is concerned those might be different types. But at the `ty::Ty` level the compiler understands that they're the same type, in-depth lifetimes, etc...

To get from a `hir::Ty` to a `ty::Ty`, you can use the `lower_ty` function outside of bodies or the `TypeckResults::node_type()` method inside of bodies.

> Warning: Don't use `lower_ty` inside of bodies, because this can cause ICEs.

## Creating Types programmatically

A common usecase for creating types programmatically is when we want to check if a type implements a trait (see Trait Checking).

Here's an example of how to create a `Ty` for a slice of `u8`, i.e. `[u8]`

    #![allow(unused)]
    fn main() {
    use rustc_middle::ty::Ty;
    // assume we have access to a LateContext
    let ty = Ty::new_slice(cx.tcx, Ty::new_u8());
    }

In general, we rely on `Ty::new_*` methods. These methods define the basic building-blocks that the type-system and trait-system use to define and understand the written code.

## Useful Links

Below are some useful links to further explore the concepts covered in this chapter:

- Stages of compilation
- Diagnostic items
- Type checking
- Ty module