| # Lints | |
| This page documents some of the machinery around lint registration and how we | |
| run lints in the compiler. | |
| The [`LintStore`] is the central piece of infrastructure, around which everything rotates. | |
| The `LintStore` is held as part of the [`Session`], and it | |
| gets populated with the list of lints shortly after the `Session` is created. | |
| ## Lints vs. lint passes | |
| There are two parts to the linting mechanism within the compiler: lints and lint passes. | |
| Unfortunately, a lot of the documentation we have refers to both of these as just "lints." | |
| First, we have the lint declarations themselves, | |
| and this is where the name and default lint level and other metadata come from. | |
| These are normally defined by way of the [`declare_lint!`] macro, | |
| which boils down to a static with type [`&rustc_lint_defs::Lint`] | |
| (although this may change in the future, | |
| as the macro is somewhat unwieldy to add new fields to, | |
| like all macros). | |
| We lint against direct declarations without the use of the macro. | |
| Lint declarations don't carry any "state" - they are merely global identifiers | |
| and descriptions of lints. | |
| We assert at runtime that they are not registered twice (by lint name). | |
| Lint passes are the meat of any lint. | |
| Notably, there is not a one-to-one | |
| relationship between lints and lint passes; a lint might not have any lint pass | |
| that emits it, it could have many, or just one -- the compiler doesn't track | |
| whether a pass is in any way associated with a particular lint, and frequently | |
| lints are emitted as part of other work (e.g., type checking, etc.). | |
| ## Registration | |
| ### High-level overview | |
| In [`rustc_interface::run_compiler`], | |
| the [`LintStore`] is created, | |
| and all lints are registered. | |
| There are three 'sources' of lints: | |
| * internal lints: lints only used by the rustc codebase | |
| * builtin lints: lints built into the compiler and not provided by some outside source | |
| * `rustc_interface::Config`[`register_lints`]: lints passed into the compiler during construction | |
| Lints are registered via the [`LintStore::register_lint`] function. | |
| This should happen just once for any lint, or an ICE will occur. | |
| Once the registration is complete, we "freeze" the lint store by placing it in an `Arc`. | |
| Lint passes are registered separately into one of the categories | |
| (pre-expansion, early, late, late module). | |
| Passes are registered as a closure | |
| -- i.e., `impl Fn() -> Box<dyn X>`, where `dyn X` is either an early or late | |
| lint pass trait object. | |
| When we run the lint passes, we run the closure and then invoke the lint pass methods. | |
| The lint pass methods take `&mut self` so they can keep track of state internally. | |
| #### Internal lints | |
| These are lints used just by the compiler or drivers like `clippy`. | |
| They can be found in [`rustc_lint::internal`]. | |
| An example of such a lint is the check that lint passes are implemented using | |
| the `declare_lint_pass!` macro and not by hand. | |
| This is accomplished with the `LINT_PASS_IMPL_WITHOUT_MACRO` lint. | |
| Registration of these lints happens in the [`rustc_lint::register_internals`] | |
| function which is called when constructing a new lint store inside [`rustc_lint::new_lint_store`]. | |
| #### Builtin Lints | |
| These are primarily described in two places, | |
| `rustc_lint_defs::builtin` and `rustc_lint::builtin`. | |
| Often the first provides the definitions for the lints themselves, | |
| and the latter provides the lint pass definitions (and implementations), | |
| but this is not always true. | |
| The builtin lint registration happens in the [`rustc_lint::register_builtins`] function. | |
| Just like with internal lints, | |
| this happens inside of [`rustc_lint::new_lint_store`]. | |
| #### Driver lints | |
| These are the lints provided by drivers via the `rustc_interface::Config` | |
| [`register_lints`] field, which is a callback. | |
| Drivers should, if finding it | |
| already set, call the function currently set within the callback they add. | |
| The best way for drivers to get access to this is by overriding the | |
| `Callbacks::config` function which gives them direct access to the `Config` structure. | |
| ## Compiler lint passes are combined into one pass | |
| Within the compiler, for performance reasons, we usually do not register dozens | |
| of lint passes. Instead, we have a single lint pass of each variety (e.g., | |
| `BuiltinCombinedLateLintModPass`) which will internally call all of the | |
| individual lint passes; this is because then we get the benefits of static over | |
| dynamic dispatch for each of the (often empty) trait methods. | |
| Ideally, we'd not have to do this, since it adds to the complexity of understanding the code. | |
| However, with the current type-erased lint store | |
| approach, it is beneficial to do so for performance reasons. | |
| [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html | |
| [`LintStore::register_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_lints | |
| [`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html | |
| [`rustc_lint::register_internals`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_internals.html | |
| [`rustc_lint::new_lint_store`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.new_lint_store.html | |
| [`declare_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_lint.html | |
| [`declare_tool_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_tool_lint.html | |
| [`register_lints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints | |
| [`&rustc_lint_defs::Lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/struct.Lint.html | |
| [`Session`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html | |
| [`rustc_interface::run_compiler`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html#reexport.run_compiler | |
| [`rustc_lint::internal`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/internal/index.html |