2023-04-10 08:59:12 +00:00
|
|
|
# Lint passes
|
|
|
|
|
|
|
|
Before working on the logic of a new lint, there is an important decision
|
|
|
|
that every Clippy developers must make: to use
|
|
|
|
[`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
|
|
|
|
|
|
|
|
In short, the `LateLintPass` has access to type 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, this is 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 boilerplates 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](define_lints.md#name-the-lint) 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>
|
|
|
|
```
|
|
|
|
|
|
|
|
## Additional Readings for Beginners
|
|
|
|
|
|
|
|
If a dear reader of this documentation has never taken a class on compilers
|
|
|
|
and interpreters, it might be confusing as to why AST level deals with only
|
|
|
|
the language's syntax. And some readers might not even understand what lexing,
|
|
|
|
parsing, and AST mean.
|
|
|
|
|
|
|
|
This documentation serves by no means as a crash course on compilers or language design.
|
|
|
|
And for details specifically related to Rust, the [Rustc Development Guide][rustc_dev_guide]
|
|
|
|
is a far better choice to peruse.
|
|
|
|
|
|
|
|
The [Syntax and AST][ast] chapter and the [High-Level IR][hir] chapter are
|
|
|
|
great introduction to the concepts mentioned in this chapter.
|
|
|
|
|
|
|
|
Some readers might also find the [introductory chapter][map_of_territory] of
|
|
|
|
Robert Nystrom's _Crafting Interpreters_ a helpful overview of compiled and
|
|
|
|
interpreted languages before jumping back to the Rustc guide.
|
|
|
|
|
|
|
|
[ast]: https://rustc-dev-guide.rust-lang.org/syntax-intro.html
|
|
|
|
[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
|
|
|
|
[hir]: https://rustc-dev-guide.rust-lang.org/hir.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
|
|
|
|
[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/
|
2023-04-10 09:09:52 +00:00
|
|
|
[map_of_territory]: https://craftinginterpreters.com/a-map-of-the-territory.html
|