From a4e29c5cb5b3dbb7fec4040a13fe0fab196461b9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 1 Jul 2021 13:56:19 +0200 Subject: [PATCH] Update section of lint store --- src/diagnostics/lintstore.md | 61 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/diagnostics/lintstore.md b/src/diagnostics/lintstore.md index b45e6055..ed2100eb 100644 --- a/src/diagnostics/lintstore.md +++ b/src/diagnostics/lintstore.md @@ -3,7 +3,7 @@ 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 +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 TyCtxt) in most code, as we need to fill it in with all of the lints, which can only happen after plugin registration. @@ -19,8 +19,7 @@ boils down to a static with type `&rustc_session::lint::Lint`. As of July 2021, we lint against direct declarations 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 -example). +the macro is somewhat unwieldy to add new fields to, like all macros). 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). @@ -34,44 +33,55 @@ lint, and frequently lints are emitted as part of other work (e.g., type checkin ### High-level overview -The lint store is created and all lints are registered during plugin registration, in -[`rustc_interface::register_plugins`]. There are three 'sources' of lint: the internal lints, plugin -lints, and `rustc_interface::Config` [`register_lints`]. All are registered here, in -`register_plugins`. +In [`rustc_interface::register_plugins`] the [`LintStore`] is created and all lints are registered. +There are four '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 +* 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 the driver, it's passed into the `GlobalCtxt` constructor where it lives in an immutable form from 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, early, late, late module). Passes are registered as a closure -- i.e., `impl Fn() -> Box`, 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, 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. #### Internal lints -Note, these include both rustc-internal lints, and the traditional lints, like, for example the dead -code lint. +These are lints used just by the compiler or plugins 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_session::lint::builtin` and -`rustc_lint::builtin`. The first provides the definitions for the lints themselves, -and the latter provides the lint pass definitions (and implementations). +`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 internal lint registration happens in the [`rustc_lint::register_builtins`] function, along with -the [`rustc_lint::register_internals`] function. More generally, the LintStore "constructor" -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. +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`]. #### Plugin lints 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 -like rustc code. Plugins are intended to declare lints with the `plugin` field set to true (e.g., by +mutable `LintStore` during registration (which happens inside of [`rustc_interface::register_plugins`]) +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; 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 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 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 approach, it is beneficial to do so for performance reasons. -New lints being added likely want to join one of the existing declarations like -`late_lint_mod_passes` in `rustc_lint/src/lib.rs`, which would then -auto-propagate into the other. - +[`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_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