Update section of lint store

This commit is contained in:
Ryan Levick 2021-07-01 13:56:19 +02:00 committed by Joshua Nelson
parent 442ba10f1e
commit a4e29c5cb5
1 changed files with 34 additions and 27 deletions

View File

@ -3,7 +3,7 @@
This page documents some of the machinery around lint registration and how we This page documents some of the machinery around lint registration and how we
run lints in the compiler. run lints in the compiler.
The `LintStore` is the central piece of infrastructure, around which everything The [`LintStore`] is the central piece of infrastructure, around which everything
rotates. It's not available during the early parts of compilation (i.e., before rotates. It's not available during the early parts of compilation (i.e., before
TyCtxt) in most code, as we need to fill it in with all of the lints, which can only happen after TyCtxt) in most code, as we need to fill it in with all of the lints, which can only happen after
plugin registration. plugin registration.
@ -19,8 +19,7 @@ boils down to a static with type `&rustc_session::lint::Lint`.
As of <!-- date: 2021-07 --> July 2021, we lint against direct declarations As of <!-- date: 2021-07 --> July 2021, we lint against direct declarations
without the use of the macro today (although this may change in the future, as without the use of the macro today (although this may change in the future, as
the macro is somewhat unwieldy to add new fields to, like all macros by the macro is somewhat unwieldy to add new fields to, like all macros).
example).
Lint declarations don't carry any "state" - they are merely global identifers and descriptions of Lint declarations don't carry any "state" - they are merely global identifers and descriptions of
lints. We assert at runtime that they are not registered twice (by lint name). lints. We assert at runtime that they are not registered twice (by lint name).
@ -34,44 +33,55 @@ lint, and frequently lints are emitted as part of other work (e.g., type checkin
### High-level overview ### High-level overview
The lint store is created and all lints are registered during plugin registration, in In [`rustc_interface::register_plugins`] the [`LintStore`] is created and all lints are registered.
[`rustc_interface::register_plugins`]. There are three 'sources' of lint: the internal lints, plugin There are four 'sources' of lints:
lints, and `rustc_interface::Config` [`register_lints`]. All are registered here, in * internal lints: lints only used by the rustc codebase
`register_plugins`. * builtin lints: lints built into the compiler and not provided by some outside source
* plugin lints: lints created by plugins through the plugin system.
* `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 `Lrc`. Later in Once the registration is complete, we "freeze" the lint store by placing it in an `Lrc`. Later in
the driver, it's passed into the `GlobalCtxt` constructor where it lives in an immutable form from the driver, it's passed into the `GlobalCtxt` constructor where it lives in an immutable form from
then on. then on.
Lints are registered via the [`LintStore::register_lint`] function. This should
happen just once for any lint, or an ICE will occur.
Lint passes are registered separately into one of the categories (pre-expansion, 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 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 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 object. When we run the lint passes, we run the closure and then invoke the lint
pass methods, which take `&mut self` -- lint passes can keep track of state pass methods. The lint pass methods take `&mut self` so they can keep track of state
internally. internally.
#### Internal lints #### Internal lints
Note, these include both rustc-internal lints, and the traditional lints, like, for example the dead These are lints used just by the compiler or plugins like `clippy`. They can be found in
code lint. `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_session::lint::builtin` and These are primarily described in two places: `rustc_session::lint::builtin` and
`rustc_lint::builtin`. The first provides the definitions for the lints themselves, `rustc_lint::builtin`. Often the first provides the definitions for the lints themselves,
and the latter provides the lint pass definitions (and implementations). and the latter provides the lint pass definitions (and implementations), but this is not always
true.
The internal lint registration happens in the [`rustc_lint::register_builtins`] function, along with The builtin lint registration happens in the [`rustc_lint::register_builtins`] function. Just like
the [`rustc_lint::register_internals`] function. More generally, the LintStore "constructor" with internal lints, this happens inside of [`rustc_lint::new_lint_store`].
function which is *the* way to get a `LintStore` in the compiler (you should not construct it
directly) is [`rustc_lint::new_lint_store`]; it calls the registration functions.
#### Plugin lints #### Plugin lints
This is one of the primary use cases remaining for plugins/drivers. Plugins are given access to the This is one of the primary use cases remaining for plugins/drivers. Plugins are given access to the
mutable `LintStore` during registration to call any functions they need on the `LintStore`, just mutable `LintStore` during registration (which happens inside of [`rustc_interface::register_plugins`])
like rustc code. Plugins are intended to declare lints with the `plugin` field set to true (e.g., by and they can call any functions they need on the `LintStore`, just like rustc code.
Plugins are intended to declare lints with the `plugin` field set to true (e.g., by
way of the [`declare_tool_lint!`] macro), but this is purely for diagnostics and help text; way of the [`declare_tool_lint!`] macro), but this is purely for diagnostics and help text;
otherwise plugin lints are mostly just as first class as rustc builtin lints. otherwise plugin lints are mostly just as first class as rustc builtin lints.
@ -86,18 +96,15 @@ within the callback they add. The best way for drivers to get access to this is
Within the compiler, for performance reasons, we usually do not register dozens 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 of lint passes. Instead, we have a single lint pass of each variety
(e.g. `BuiltinCombinedModuleLateLintPass`) which will internally call all of the (e.g., `BuiltinCombinedModuleLateLintPass`) which will internally call all of the
individual lint passes; this is because then we get the benefits of static over individual lint passes; this is because then we get the benefits of static over
dynamic dispatch for each of the (often empty) trait methods. dynamic dispatch for each of the (often empty) trait methods.
Ideally, we'd not have to do this, since it certainly adds to the complexity of 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 understanding the code. However, with the current type-erased lint store
approach, it is beneficial to do so for performance reasons. approach, it is beneficial to do so for performance reasons.
New lints being added likely want to join one of the existing declarations like [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
`late_lint_mod_passes` in `rustc_lint/src/lib.rs`, which would then
auto-propagate into the other.
[`LintStore::register_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_lints [`LintStore::register_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_lints
[`rustc_interface::register_plugins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.register_plugins.html [`rustc_interface::register_plugins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.register_plugins.html
[`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html [`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html