Skip to content

Commit c993201

Browse files
committed
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.
1 parent 6ba0ce4 commit c993201

File tree

18 files changed

+137
-83
lines changed

18 files changed

+137
-83
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3798,6 +3798,7 @@ dependencies = [
37983798
"annotate-snippets 0.11.5",
37993799
"derive_setters",
38003800
"rustc_abi",
3801+
"rustc_ast",
38013802
"rustc_data_structures",
38023803
"rustc_error_codes",
38033804
"rustc_error_messages",

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}
2626
use rustc_ast::*;
2727
use rustc_ast_pretty::pprust::{self, State};
2828
use rustc_data_structures::fx::FxIndexMap;
29-
use rustc_errors::DiagCtxtHandle;
29+
use rustc_errors::{DiagCtxtHandle, LintBuffer};
3030
use rustc_feature::Features;
3131
use rustc_parse::validate_attr;
3232
use rustc_session::Session;
33+
use rustc_session::lint::BuiltinLintDiag;
3334
use rustc_session::lint::builtin::{
3435
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
3536
PATTERNS_IN_FNS_WITHOUT_BODY,
3637
};
37-
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
3838
use rustc_span::{Ident, Span, kw, sym};
3939
use rustc_target::spec::{AbiMap, AbiMapping};
4040
use thin_vec::thin_vec;

compiler/rustc_builtin_macros/src/format.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ use rustc_ast::{
1010
};
1111
use rustc_data_structures::fx::FxHashSet;
1212
use rustc_errors::{
13-
Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize,
13+
Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify,
14+
pluralize,
1415
};
1516
use rustc_expand::base::*;
1617
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
17-
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
18+
use rustc_lint_defs::{BuiltinLintDiag, LintId};
1819
use rustc_parse::exp;
1920
use rustc_parse_format as parse;
2021
use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol};
@@ -595,7 +596,8 @@ fn make_format_args(
595596
named_arg_sp: arg_name.span,
596597
named_arg_name: arg_name.name.to_string(),
597598
is_formatting_arg: matches!(used_as, Width | Precision),
598-
},
599+
}
600+
.into(),
599601
});
600602
}
601603
}

compiler/rustc_errors/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2024"
88
annotate-snippets = "0.11"
99
derive_setters = "0.1.6"
1010
rustc_abi = { path = "../rustc_abi" }
11+
rustc_ast = { path = "../rustc_ast" }
1112
rustc_data_structures = { path = "../rustc_data_structures" }
1213
rustc_error_codes = { path = "../rustc_error_codes" }
1314
rustc_error_messages = { path = "../rustc_error_messages" }
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/// This module provides types and traits for buffering lints until later in compilation.
2+
use rustc_ast::node_id::NodeId;
3+
use rustc_data_structures::fx::FxIndexMap;
4+
use rustc_error_messages::MultiSpan;
5+
use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId};
6+
7+
use crate::{DynSend, LintDiagnostic, LintDiagnosticBox};
8+
9+
/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its
10+
/// variants requires types we don't have yet. So, handle that case separately.
11+
pub enum DecorateDiagCompat {
12+
Dynamic(Box<dyn for<'a> LintDiagnosticBox<'a, ()> + DynSend + 'static>),
13+
Builtin(BuiltinLintDiag),
14+
}
15+
16+
impl std::fmt::Debug for DecorateDiagCompat {
17+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18+
f.debug_struct("DecorateDiagCompat").finish()
19+
}
20+
}
21+
22+
impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {}
23+
24+
impl<D: for<'a> LintDiagnostic<'a, ()> + DynSend + 'static> From<D> for DecorateDiagCompat {
25+
#[inline]
26+
fn from(d: D) -> Self {
27+
Self::Dynamic(Box::new(d))
28+
}
29+
}
30+
31+
impl From<BuiltinLintDiag> for DecorateDiagCompat {
32+
#[inline]
33+
fn from(b: BuiltinLintDiag) -> Self {
34+
Self::Builtin(b)
35+
}
36+
}
37+
38+
/// Lints that are buffered up early on in the `Session` before the
39+
/// `LintLevels` is calculated.
40+
#[derive(Debug)]
41+
pub struct BufferedEarlyLint {
42+
/// The span of code that we are linting on.
43+
pub span: Option<MultiSpan>,
44+
45+
/// The `NodeId` of the AST node that generated the lint.
46+
pub node_id: NodeId,
47+
48+
/// A lint Id that can be passed to
49+
/// `rustc_lint::early::EarlyContextAndPass::check_id`.
50+
pub lint_id: LintId,
51+
52+
/// Customization of the `Diag<'_>` for the lint.
53+
pub diagnostic: DecorateDiagCompat,
54+
}
55+
56+
#[derive(Default, Debug)]
57+
pub struct LintBuffer {
58+
pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
59+
}
60+
61+
impl LintBuffer {
62+
pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
63+
self.map.entry(early_lint.node_id).or_default().push(early_lint);
64+
}
65+
66+
pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
67+
// FIXME(#120456) - is `swap_remove` correct?
68+
self.map.swap_remove(&id).unwrap_or_default()
69+
}
70+
71+
pub fn buffer_lint(
72+
&mut self,
73+
lint: &'static Lint,
74+
node_id: NodeId,
75+
span: impl Into<MultiSpan>,
76+
decorate: impl Into<DecorateDiagCompat>,
77+
) {
78+
self.add_early_lint(BufferedEarlyLint {
79+
lint_id: LintId::of(lint),
80+
node_id,
81+
span: Some(span.into()),
82+
diagnostic: decorate.into(),
83+
});
84+
}
85+
}

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,20 @@ where
138138
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
139139
#[rustc_diagnostic_item = "LintDiagnostic"]
140140
pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
141-
/// Decorate and emit a lint.
141+
/// Decorate a lint with the information from this type.
142142
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
143143
}
144144

145+
pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> {
146+
fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>);
147+
}
148+
149+
impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D {
150+
fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>) {
151+
self.decorate_lint(diag);
152+
}
153+
}
154+
145155
#[derive(Clone, Debug, Encodable, Decodable)]
146156
pub(crate) struct DiagLocation {
147157
file: Cow<'static, str>,

compiler/rustc_errors/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ use std::{fmt, panic};
4040

4141
use Level::*;
4242
pub use codes::*;
43+
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
4344
pub use diagnostic::{
4445
BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee,
45-
FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic,
46+
FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic,
4647
};
4748
pub use diagnostic_impls::{
4849
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
@@ -80,6 +81,7 @@ use crate::timings::TimingRecord;
8081

8182
pub mod annotate_snippet_emitter_writer;
8283
pub mod codes;
84+
mod decorate_diag;
8385
mod diagnostic;
8486
mod diagnostic_impls;
8587
pub mod emitter;

compiler/rustc_expand/src/base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ use rustc_ast::visit::{AssocCtxt, Visitor};
1313
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
1414
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
1515
use rustc_data_structures::sync;
16-
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
16+
use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
1717
use rustc_feature::Features;
1818
use rustc_hir as hir;
1919
use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation};
2020
use rustc_hir::def::MacroKinds;
2121
use rustc_hir::{Stability, find_attr};
22-
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
22+
use rustc_lint_defs::RegisteredTools;
2323
use rustc_parse::MACRO_ARGUMENTS;
2424
use rustc_parse::parser::{ForceCollect, Parser};
2525
use rustc_session::config::CollapseMacroDebuginfo;

compiler/rustc_interface/src/util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use rustc_ast as ast;
88
use rustc_codegen_ssa::traits::CodegenBackend;
99
use rustc_data_structures::jobserver::Proxy;
1010
use rustc_data_structures::sync;
11+
use rustc_errors::LintBuffer;
1112
use rustc_metadata::{DylibError, load_symbol_from_dylib};
1213
use rustc_middle::ty::CurrentGcx;
1314
use rustc_parse::validate_attr;
1415
use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
15-
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
16+
use rustc_session::lint::{self, BuiltinLintDiag};
1617
use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
1718
use rustc_session::{EarlyDiagCtxt, Session, filesearch};
1819
use rustc_span::edit_distance::find_best_match_for_name;

compiler/rustc_lint/src/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence;
1111
use rustc_data_structures::fx::FxIndexMap;
1212
use rustc_data_structures::sync;
1313
use rustc_data_structures::unord::UnordMap;
14-
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
14+
use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan};
1515
use rustc_feature::Features;
1616
use rustc_hir::def::Res;
1717
use rustc_hir::def_id::{CrateNum, DefId};
@@ -23,7 +23,7 @@ use rustc_middle::middle::privacy::EffectiveVisibilities;
2323
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
2424
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
2525
use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
26-
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
26+
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId};
2727
use rustc_session::{DynLintStore, Session};
2828
use rustc_span::edit_distance::find_best_match_for_names;
2929
use rustc_span::{Ident, Span, Symbol, sym};

0 commit comments

Comments
 (0)