- Late lint pass: Works on [HIR nodes], towards the end of [analysis] (after
borrow checking, etc.). These lints have full type information available.
Most lints are late.
- Example: The [`invalid_value`] lint (which checks for obviously invalid
uninitialized values) is a late lint because it needs type information to
figure out whether a type allows being left uninitialized.
- MIR pass: Works on [MIR nodes]. This isn't quite the same as other passes;
lints that work on MIR nodes have their own methods for running.
- Example: The [`arithmetic_overflow`] lint is emitted when it detects a
constant value that may overflow.
Most lints work well via the pass systems, and they have a fairly
straightforward interface and easy way to integrate (mostly just implementing
a specific `check` function). However, some lints are easier to write when
they live on a specific code path anywhere in the compiler. For example, the
[`unused_mut`] lint is implemented in the borrow checker as it requires some
information and state in the borrow checker.
Some of these inline lints fire before the linting system is ready. Those
lints will be *buffered* where they are held until later phases of the
compiler when the linting system is ready. See [Linting early in the
compiler](#linting-early-in-the-compiler).
[AST nodes]: the-parser.md
[AST lowering]: ast-lowering.md
[HIR nodes]: hir.md
[MIR nodes]: mir/index.md
[macro expansion]: macro-expansion.md
[analysis]: part-4-intro.md
[`keyword_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#keyword-idents
[`unused_parens`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-parens
[`invalid_value`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#invalid-value
[`arithmetic_overflow`]: https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#arithmetic-overflow
[`unused_mut`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unused-mut
### Lint definition terms
Lints are managed via the [`LintStore`][LintStore] and get registered in
various ways. The following terms refer to the different classes of lints
generally based on how they are registered.
- *Built-in* lints are defined inside the compiler source.
- *Driver-registered* lints are registered when the compiler driver is created
by an external driver. This is the mechanism used by Clippy, for example.
- *Tool* lints are lints with a path prefix like `clippy::` or `rustdoc::`.
- *Internal* lints are the `rustc::` scoped tool lints that only run on the
rustc source tree itself and are defined in the compiler source like a
regular built-in lint.
More information about lint registration can be found in the [LintStore]
chapter.
[LintStore]: diagnostics/lintstore.md
### Declaring a lint
The built-in compiler lints are defined in the [`rustc_lint`][builtin]
crate. Lints that need to be implemented in other crates are defined in
[`rustc_lint_defs`]. You should prefer to place lints in `rustc_lint` if
possible. One benefit is that it is close to the dependency root, so it can be
much faster to work on.
[builtin]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html
[`rustc_lint_defs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/index.html
Every lint is implemented via a `struct` that implements the `LintPass` `trait`
(you can also implement one of the more specific lint pass traits, either
`EarlyLintPass` or `LateLintPass` depending on when is best for your lint to run).
The trait implementation allows you to check certain syntactic constructs
as the linter walks the AST. You can then choose to emit lints in a
very similar way to compile errors.
You also declare the metadata of a particular lint via the [`declare_lint!`]
macro. This macro includes the name, the default level, a short description, and some
more details.
Note that the lint and the lint pass must be registered with the compiler.