forked from rust-lang/rust-clippy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#10622 - blyxyas:book-lint_passes, r=llogiq
Clippy Book Chapter Updates Reborn: Lint Passes This PR adds a new chapter to the book: "Lint passes". No major changes apart from some re-phrasing, fixing typos... etc. ## Notes - Requires rust-lang#10595 to be merged before this one (Or else, a link will be broken). - To talk about the whole project, please use the tracking issue for the project rust-lang#10597 (It also contains a timeline, discussions and more information) changelog: Add a new "Lint passes" chapter to the book r? `@flip1995`
- Loading branch information
Showing
1 changed file
with
136 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# 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 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](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/ | ||
[map_of_territory]: https://craftinginterpreters.com/a-map-of-the-territory.html |