|
| 1 | +# Lints |
| 2 | + |
| 3 | +This page documents some of the machinery around lint registration and how we |
| 4 | +run lints in the compiler. |
| 5 | + |
| 6 | +The `LintStore` is the central piece of infrastructure, around which everything |
| 7 | +rotates. It's not available during the early parts of compilation (i.e., before |
| 8 | +TyCtxt) in most code, as we need to fill it in with all of the lints, which can only happen after |
| 9 | +plugin registration. |
| 10 | + |
| 11 | +## Lints vs. lint passes |
| 12 | + |
| 13 | +There are two parts to the linting mechanism within the compiler: lints and lint passes. |
| 14 | +Unfortunately, a lot of the documentation we have refers to both of these as just "lints." |
| 15 | + |
| 16 | +First, we have the lint declarations themselves: this is where the name and default lint level and |
| 17 | +other metadata come from. These are normally defined by way of the [`declare_lint!`] macro, which |
| 18 | +boils down to a static with type `&rustc::lint::Lint`. We lint against direct declarations without |
| 19 | +the use of the macro today (though this may change in the future, as the macro is somewhat unwieldy |
| 20 | +to add new fields to, like all macros by example). |
| 21 | + |
| 22 | +Lint declarations don't carry any "state" - they are merely global identifers and descriptions of |
| 23 | +lints. We assert at runtime that they are not registered twice (by lint name). |
| 24 | + |
| 25 | +Lint passes are the meat of any lint. Notably, there is not a one-to-one relationship between |
| 26 | +lints and lint passes; a lint might not have any lint pass that emits it, it could have many, or |
| 27 | +just one -- the compiler doesn't track whether a pass is in any way associated with a particular |
| 28 | +lint, and frequently lints are emitted as part of other work (e.g., type checking, etc.). |
| 29 | + |
| 30 | +## Registration |
| 31 | + |
| 32 | +### High-level overview |
| 33 | + |
| 34 | +The lint store is created and all lints are registered during plugin registration, in |
| 35 | +[`rustc_interface::register_plugins`]. There are three 'sources' of lint: the internal lints, plugin |
| 36 | +lints, and `rustc_interface::Config` `register_lints`. All are registered here, in |
| 37 | +`register_plugins`. |
| 38 | + |
| 39 | +Once the registration is complete, we "freeze" the lint store by placing it in an `Lrc`. Later in |
| 40 | +the driver, it's passed into the `GlobalCtxt` constructor where it lives in an immutable form from |
| 41 | +then on. |
| 42 | + |
| 43 | +Lints are registered via the [`LintStore::register_lint`] function. This should |
| 44 | +happen just once for any lint, or an ICE will occur. |
| 45 | + |
| 46 | +Lint passes are registered separately into one of the categories (pre-expansion, |
| 47 | +early, late, late module). Passes are registered as a closure -- i.e., `impl |
| 48 | +Fn() -> Box<dyn X>`, where `dyn X` is either an early or late lint pass trait |
| 49 | +object. When we run the lint passes, we run the closure and then invoke the lint |
| 50 | +pass methods, which take `&mut self` -- lint passes can keep track of state |
| 51 | +internally. |
| 52 | + |
| 53 | +#### Internal lints |
| 54 | + |
| 55 | +Note, these include both rustc-internal lints, and the traditional lints, like, for example the dead |
| 56 | +code lint. |
| 57 | + |
| 58 | +These are primarily described in two places: `rustc::lint::builtin` and `rustc_lint::builtin`. The |
| 59 | +first provides the definitions for the lints themselves, and the latter provides the lint pass |
| 60 | +definitions (and implementations). |
| 61 | + |
| 62 | +The internal lint registration happens in the [`rustc_lint::register_builtins`] function, along with |
| 63 | +the [`rustc_lint::register_internals`] function. More generally, the LintStore "constructor" |
| 64 | +function which is *the* way to get a `LintStore` in the compiler (you should not construct it |
| 65 | +directly) is [`rustc_lint::new_lint_store`]; it calls the registration functions. |
| 66 | + |
| 67 | +#### Plugin lints |
| 68 | + |
| 69 | +This is one of the primary use cases remaining for plugins/drivers. Plugins are given access to the |
| 70 | +mutable `LintStore` during registration to call any functions they need on the `LintStore`, just |
| 71 | +like rustc code. Plugins are intended to declare lints with the `plugin` field set to true (e.g., by |
| 72 | +way of the [`declare_tool_lint!`] macro), but this is purely for diagnostics and help text; |
| 73 | +otherwise plugin lints are mostly just as first class as rustc builtin lints. |
| 74 | + |
| 75 | +#### Driver lints |
| 76 | + |
| 77 | +These are the lints provided by drivers via the `rustc_interface::Config` [`register_lints`] field, |
| 78 | +which is a callback. Drivers should, if finding it already set, call the function currently set |
| 79 | +within the callback they add. The best way for drivers to get access to this is by overriding the |
| 80 | +`Callbacks::config` function which gives them direct access to the `Config` structure. |
| 81 | + |
| 82 | +## Compiler lint passes are combined into one pass |
| 83 | + |
| 84 | +Within the compiler, for performance reasons, we usually do not register dozens |
| 85 | +of lint passes. Instead, we have a single lint pass of each variety |
| 86 | +(e.g. `BuiltinCombinedModuleLateLintPass`) which will internally call all of the |
| 87 | +individual lint passes; this is because then we get the benefits of static over |
| 88 | +dynamic dispatch for each of the (often empty) trait methods. |
| 89 | + |
| 90 | +Ideally, we'd not have to do this, since it certainly adds to the complexity of |
| 91 | +understanding the code. However, with the current type-erased lint store |
| 92 | +approach, it is beneficial to do so for performance reasons. |
| 93 | + |
| 94 | +New lints being added likely want to join one of the existing declarations like |
| 95 | +`late_lint_mod_passes` in `librustc_lint/lib.rs`, which would then |
| 96 | +auto-propagate into the other. |
| 97 | + |
| 98 | +[`LintStore::register_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/lint/struct.LintStore.html#method.register_lints |
| 99 | +[`rustc_interface::register_plugins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.register_plugins.html) |
| 100 | +[`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html |
| 101 | +[`rustc_lint::register_internals`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_internals.html |
| 102 | +[`rustc_lint::new_lint_store`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.new_lint_store.html |
| 103 | +[`rustc::declare_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/macro.declare_lint.html |
| 104 | +[`rustc::declare_tool_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/macro.declare_tool_lint.html |
| 105 | +[`register_lints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints |
0 commit comments