diff options
| author | Josh Triplett <josh@joshtriplett.org> | 2025-08-21 20:30:28 -0700 |
|---|---|---|
| committer | Josh Triplett <josh@joshtriplett.org> | 2025-08-22 01:59:56 -0700 |
| commit | c99320156dfeea98836fe7dad97d16f67c4d879e (patch) | |
| tree | 24c7eff6f2a50bc1480d10bdf009d448be90cf91 /compiler/rustc_errors/src | |
| parent | 6ba0ce40941eee1ca02e9ba49c791ada5158747a (diff) | |
| download | rust-c99320156dfeea98836fe7dad97d16f67c4d879e.tar.gz rust-c99320156dfeea98836fe7dad97d16f67c4d879e.zip | |
Refactor lint buffering to avoid requiring a giant enum
Lint buffering currently relies on a giant enum `BuiltinLintDiag` containing all the lints that might potentially get buffered. In addition to being an unwieldy enum in a central crate, this also makes `rustc_lint_defs` a build bottleneck: it depends on various types from various crates (with a steady pressure to add more), and many crates depend on it. Having all of these variants in a separate crate also prevents detecting when a variant becomes unused, which we can do with a dedicated type defined and used in the same crate. Refactor this to use a dyn trait, to allow using `LintDiagnostic` types directly. This requires boxing, but all of this is already on the slow path (emitting an error). Because the existing `BuiltinLintDiag` requires some additional types in order to decorate some variants, which are only available later in `rustc_lint`, use an enum `DecorateDiagCompat` to handle both the `dyn LintDiagnostic` case and the `BuiltinLintDiag` case.
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/decorate_diag.rs | 85 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 4 |
3 files changed, 99 insertions, 2 deletions
diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs new file mode 100644 index 00000000000..5aef26ccf97 --- /dev/null +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -0,0 +1,85 @@ +/// This module provides types and traits for buffering lints until later in compilation. +use rustc_ast::node_id::NodeId; +use rustc_data_structures::fx::FxIndexMap; +use rustc_error_messages::MultiSpan; +use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; + +use crate::{DynSend, LintDiagnostic, LintDiagnosticBox}; + +/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// variants requires types we don't have yet. So, handle that case separately. +pub enum DecorateDiagCompat { + Dynamic(Box<dyn for<'a> LintDiagnosticBox<'a, ()> + DynSend + 'static>), + Builtin(BuiltinLintDiag), +} + +impl std::fmt::Debug for DecorateDiagCompat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DecorateDiagCompat").finish() + } +} + +impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {} + +impl<D: for<'a> LintDiagnostic<'a, ()> + DynSend + 'static> From<D> for DecorateDiagCompat { + #[inline] + fn from(d: D) -> Self { + Self::Dynamic(Box::new(d)) + } +} + +impl From<BuiltinLintDiag> for DecorateDiagCompat { + #[inline] + fn from(b: BuiltinLintDiag) -> Self { + Self::Builtin(b) + } +} + +/// Lints that are buffered up early on in the `Session` before the +/// `LintLevels` is calculated. +#[derive(Debug)] +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: Option<MultiSpan>, + + /// The `NodeId` of the AST node that generated the lint. + pub node_id: NodeId, + + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. + pub lint_id: LintId, + + /// Customization of the `Diag<'_>` for the lint. + pub diagnostic: DecorateDiagCompat, +} + +#[derive(Default, Debug)] +pub struct LintBuffer { + pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>, +} + +impl LintBuffer { + pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { + self.map.entry(early_lint.node_id).or_default().push(early_lint); + } + + pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> { + // FIXME(#120456) - is `swap_remove` correct? + self.map.swap_remove(&id).unwrap_or_default() + } + + pub fn buffer_lint( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: impl Into<MultiSpan>, + decorate: impl Into<DecorateDiagCompat>, + ) { + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: Some(span.into()), + diagnostic: decorate.into(), + }); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 183dceddd2c..43ce886975c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -138,10 +138,20 @@ where /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. #[rustc_diagnostic_item = "LintDiagnostic"] pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate and emit a lint. + /// Decorate a lint with the information from this type. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); } +pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> { + fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>); +} + +impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D { + fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>) { + self.decorate_lint(diag); + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub(crate) struct DiagLocation { file: Cow<'static, str>, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a775b70dbee..38c5716348f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -40,9 +40,10 @@ use std::{fmt, panic}; use Level::*; pub use codes::*; +pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, - FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic, + FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -80,6 +81,7 @@ use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; +mod decorate_diag; mod diagnostic; mod diagnostic_impls; pub mod emitter; |
