rust-clippy/book/src/development/lint_passes.md
Sergen Karaoglan d1f55e6b53 run linkcheck in Remark CI
fix new lints link

install nightly rust-docs

run linkcheck without nightly toolchain

remove nightly toolchain, add rust-docs component

Test Remark

Update basics.md

Update basics.md

Update basics.md

update workflow

add rust docs toolchain

Update remark.yml

workflow test

manual test

update book path

add linkcheck book to CI

Update lint_passes.md
2023-04-28 15:11:48 +01:00

114 lines
4.9 KiB
Markdown

# Lint passes
Before working on the logic of a new lint, there is an important decision
that every Clippy developer must make: to use
[`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
In short, the `LateLintPass` has access to type and symbol information while the
`EarlyLintPass` doesn't. If you don't need access to type information, use the
`EarlyLintPass`.
Let us expand on these two traits more below.
## `EarlyLintPass`
If you examine the documentation on [`EarlyLintPass`][early_lint_pass] closely,
you'll see that every method defined for this trait utilizes a
[`EarlyContext`][early_context]. In `EarlyContext`'s documentation, it states:
> Context for lint checking of the AST, after expansion, before lowering to HIR.
Voilà. `EarlyLintPass` works only on the Abstract Syntax Tree (AST) level.
And AST is generated during the [lexing and parsing][lexing_and_parsing] phase
of code compilation. Therefore, it doesn't know what a symbol means or information about types, and it should
be our trait choice for a new lint if the lint only deals with syntax-related issues.
While linting speed has not been a concern for Clippy,
the `EarlyLintPass` is faster, and it should be your choice
if you know for sure a lint does not need type information.
As a reminder, run the following command to generate boilerplate for lints
that use `EarlyLintPass`:
```sh
$ cargo dev new_lint --name=<your_new_lint> --pass=early --category=<your_category_choice>
```
### Example for `EarlyLintPass`
Take a look at the following code:
```rust
let x = OurUndefinedType;
x.non_existing_method();
```
From the AST perspective, both lines are "grammatically" correct.
The assignment uses a `let` and ends with a semicolon. The invocation
of a method looks fine, too. As programmers, we might raise a few
questions already, but the parser is okay with it. This is what we
mean when we say `EarlyLintPass` deals with only syntax on the AST level.
Alternatively, think of the `foo_functions` lint we mentioned in
define new lints <!-- FIXME: add link --> chapter.
We want the `foo_functions` lint to detect functions with `foo` as their name.
Writing a lint that only checks for the name of a function means that we only
work with the AST and don't have to access the type system at all (the type system is where
`LateLintPass` comes into the picture).
## `LateLintPass`
In contrast to `EarlyLintPass`, `LateLintPass` contains type information.
If you examine the documentation on [`LateLintPass`][late_lint_pass] closely,
you see that every method defined in this trait utilizes a
[`LateContext`][late_context].
In `LateContext`'s documentation we will find methods that
deal with type-checking, which do not exist in `EarlyContext`, such as:
- [`maybe_typeck_results`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#method.maybe_typeck_results)
- [`typeck_results`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#method.typeck_results)
### Example for `LateLintPass`
Let us take a look with the following example:
```rust
let x = OurUndefinedType;
x.non_existing_method();
```
These two lines of code are syntactically correct code from the perspective
of the AST. We have an assignment and invoke a method on the variable that
is of a type. Grammatically, everything is in order for the parser.
However, going down a level and looking at the type information,
the compiler will notice that both `OurUndefinedType` and `non_existing_method()`
**are undefined**.
As Clippy developers, to access such type information, we must implement
`LateLintPass` on our lint.
When you browse through Clippy's lints, you will notice that almost every lint
is implemented in a `LateLintPass`, specifically because we often need to check
not only for syntactic issues but also type information.
Another limitation of the `EarlyLintPass` is that the nodes are only identified
by their position in the AST. This means that you can't just get an `id` and
request a certain node. For most lints that is fine, but we have some lints
that require the inspection of other nodes, which is easier at the HIR level.
In these cases, `LateLintPass` is the better choice.
As a reminder, run the following command to generate boilerplate for lints
that use `LateLintPass`:
```sh
$ cargo dev new_lint --name=<your_new_lint> --pass=late --category=<your_category_choice>
```
[early_context]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.EarlyContext.html
[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
[late_context]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
[lexing_and_parsing]: https://rustc-dev-guide.rust-lang.org/overview.html#lexing-and-parsing