Set up API to make it possible to pass closures instead of AttributeLint.

The end goal being to completely remove `AttributeLint`.
This commit is contained in:
Guillaume Gomez
2026-03-25 15:03:46 +01:00
parent f29256dd14
commit c5b9918540
15 changed files with 133 additions and 64 deletions
+1
View File
@@ -4004,6 +4004,7 @@ dependencies = [
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_error_messages",
"rustc_errors",
"rustc_hashes",
"rustc_hir_id",
"rustc_index",
+19 -9
View File
@@ -41,7 +41,7 @@
use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::Visitor;
use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, Late, OmitDoc};
use rustc_attr_parsing::{AttributeParser, EmitAttribute, Late, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::sorted_map::SortedMap;
@@ -52,7 +52,7 @@
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
use rustc_hir::definitions::PerParentDisambiguatorState;
use rustc_hir::lints::{AttributeLint, DelayedLint};
use rustc_hir::lints::{AttributeLint, DelayedLint, DynAttribute};
use rustc_hir::{
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr,
@@ -1174,13 +1174,23 @@ fn lower_attrs_vec(
target,
OmitDoc::Lower,
|s| l.lower(s),
|lint_id, span, kind| {
self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint {
lint_id,
id: target_hir_id,
span,
kind,
}));
|lint_id, span, kind| match kind {
EmitAttribute::Static(attr_kind) => {
self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint {
lint_id,
id: target_hir_id,
span,
kind: attr_kind,
}));
}
EmitAttribute::Dynamic(callback) => {
self.delayed_lints.push(DelayedLint::Dynamic(DynAttribute {
lint_id,
id: target_hir_id,
span,
callback,
}));
}
},
)
}
+3 -3
View File
@@ -17,7 +17,6 @@
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use crate::AttributeParser;
// Glob imports to avoid big, bitrotty import lists
use crate::attributes::allow_unstable::*;
use crate::attributes::autodiff::*;
@@ -66,6 +65,7 @@
ParsedDescription,
};
use crate::target_checking::AllowedTargets;
use crate::{AttributeParser, EmitAttribute};
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
@@ -473,7 +473,7 @@ pub(crate) fn emit_lint<M: Into<MultiSpan>>(
) {
return;
}
(self.emit_lint)(LintId::of(lint), span.into(), kind);
(self.emit_lint)(LintId::of(lint), span.into(), EmitAttribute::Static(kind));
}
pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
@@ -569,7 +569,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
/// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S`
/// is `Late` and is the ID of the syntactical component this attribute was applied to.
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, MultiSpan, AttributeLintKind),
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, MultiSpan, EmitAttribute),
}
/// Context given to every attribute parser during finalization.
@@ -57,3 +57,12 @@ pub(crate) struct MustBeNameOfAssociatedFunction {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("unsafe attribute used without unsafe")]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label("usage of unsafe attribute")]
pub span: Span,
#[subdiagnostic]
pub suggestion: Option<crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion>,
}
+28 -6
View File
@@ -3,7 +3,8 @@
use rustc_ast as ast;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
use rustc_errors::{DiagCtxtHandle, MultiSpan};
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_errors::{Diag, DiagCtxtHandle, Level, MultiSpan};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
@@ -19,6 +20,15 @@
use crate::session_diagnostics::ParsedDescription;
use crate::{Early, Late, OmitDoc, ShouldEmit};
pub enum EmitAttribute {
Static(AttributeLintKind),
Dynamic(
Box<
dyn for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static,
>,
),
}
/// Context created once, for example as part of the ast lowering
/// context, through which all attributes can be lowered.
pub struct AttributeParser<'sess, S: Stage = Late> {
@@ -119,7 +129,14 @@ pub fn parse_limited_all(
target,
OmitDoc::Skip,
std::convert::identity,
|lint_id, span, kind| sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind),
|lint_id, span, kind| match kind {
EmitAttribute::Static(kind) => {
sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind)
}
EmitAttribute::Dynamic(callback) => {
sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback)
}
},
)
}
@@ -199,8 +216,13 @@ pub fn parse_single_args<T, I>(
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: AttributeLintKind| {
sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind)
let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| match kind {
EmitAttribute::Static(kind) => {
sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind)
}
EmitAttribute::Dynamic(callback) => {
sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback)
}
};
if let Some(safety) = attr_safety {
parser.check_attribute_safety(
@@ -209,7 +231,7 @@ pub fn parse_single_args<T, I>(
safety,
expected_safety,
&mut emit_lint,
)
);
}
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
@@ -266,7 +288,7 @@ pub fn parse_attribute_list(
target: Target,
omit_doc: OmitDoc,
lower_span: impl Copy + Fn(Span) -> Span,
mut emit_lint: impl FnMut(LintId, MultiSpan, AttributeLintKind),
mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
) -> Vec<Attribute> {
let mut attributes = Vec::new();
// We store the attributes we intend to discard at the end of this function in order to
+1 -1
View File
@@ -113,5 +113,5 @@
pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;
pub use interface::{AttributeParser, EmitAttribute};
pub use session_diagnostics::ParsedDescription;
+14 -9
View File
@@ -1,14 +1,13 @@
use rustc_ast::Safety;
use rustc_errors::MultiSpan;
use rustc_errors::{Diagnostic, MultiSpan};
use rustc_hir::AttrPath;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::LintId;
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
use rustc_span::Span;
use crate::attributes::AttributeSafety;
use crate::context::Stage;
use crate::{AttributeParser, ShouldEmit};
use crate::{AttributeParser, EmitAttribute, ShouldEmit, errors};
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn check_attribute_safety(
@@ -17,7 +16,7 @@ pub fn check_attribute_safety(
attr_span: Span,
attr_safety: Safety,
expected_safety: AttributeSafety,
emit_lint: &mut impl FnMut(LintId, MultiSpan, AttributeLintKind),
emit_lint: &mut impl FnMut(LintId, MultiSpan, EmitAttribute),
) {
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
@@ -80,11 +79,17 @@ pub fn check_attribute_safety(
emit_lint(
LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
path_span.into(),
AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
},
EmitAttribute::Dynamic(Box::new(move |dcx, level| {
errors::UnsafeAttrOutsideUnsafeLint {
span: path_span,
suggestion: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()))
.map(|(left, right)| {
crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right }
}),
}
.into_diag(dcx, level)
})),
)
}
}
+23
View File
@@ -7,6 +7,7 @@
use std::path::PathBuf;
use std::thread::panicking;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_macros::{Decodable, Encodable};
@@ -118,6 +119,28 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
}
}
impl<'a> Diagnostic<'a, ()>
for Box<
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSync + DynSend + 'static,
>
{
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
self(dcx, level)
}
}
pub struct DiagCallback<'a>(
pub &'a Box<
dyn for<'b> Fn(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSend + DynSync + 'static,
>,
);
impl<'a, 'b> Diagnostic<'a, ()> for DiagCallback<'b> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
(self.0)(dcx, level)
}
}
/// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait.
pub struct DiagDecorator<F: FnOnce(&mut Diag<'_, ()>)>(pub F);
+2 -2
View File
@@ -36,8 +36,8 @@
pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic,
EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic,
BugAbort, Diag, DiagCallback, DiagDecorator, DiagInner, DiagLocation, DiagStyledString,
Diagnostic, EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
+1
View File
@@ -13,6 +13,7 @@ rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_error_messages = { path = "../rustc_error_messages" }
rustc_errors = { path = "../rustc_errors" }
rustc_hashes = { path = "../rustc_hashes" }
rustc_hir_id = { path = "../rustc_hir_id" }
rustc_index = { path = "../rustc_index" }
+25 -3
View File
@@ -1,4 +1,6 @@
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_error_messages::MultiSpan;
use rustc_errors::{Diag, DiagCtxtHandle, Level};
use rustc_lint_defs::LintId;
pub use rustc_lint_defs::{AttributeLintKind, FormatWarning};
@@ -14,13 +16,33 @@
/// AST lowering to be emitted once HIR is built.
#[derive(Debug)]
pub enum DelayedLint {
AttributeParsing(AttributeLint<HirId>),
AttributeParsing(AttributeLint),
Dynamic(DynAttribute),
}
#[derive(Debug)]
pub struct AttributeLint<Id> {
pub struct AttributeLint {
pub lint_id: LintId,
pub id: Id,
pub id: HirId,
pub span: MultiSpan,
pub kind: AttributeLintKind,
}
pub struct DynAttribute {
pub lint_id: LintId,
pub id: HirId,
pub span: MultiSpan,
pub callback: Box<
dyn for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static,
>,
}
impl std::fmt::Debug for DynAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynAttribute")
.field("lint_id", &self.lint_id)
.field("id", &self.id)
.field("span", &self.span)
.finish()
}
}
+7
View File
@@ -13,6 +13,7 @@
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal, par_fns};
use rustc_data_structures::thousands;
use rustc_errors::DiagCallback;
use rustc_errors::timings::TimingSection;
use rustc_expand::base::{ExtCtxt, LintStoreExpand};
use rustc_feature::Features;
@@ -1044,6 +1045,12 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) {
},
);
}
DelayedLint::Dynamic(attribute_lint) => tcx.emit_node_span_lint(
attribute_lint.lint_id.lint,
attribute_lint.id,
attribute_lint.span.clone(),
DiagCallback(&attribute_lint.callback),
),
}
}
}
@@ -82,15 +82,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
target,
}
.into_diag(dcx, level),
&AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => {
lints::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: sugg_spans.map(|(left, right)| {
lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }
}),
}
.into_diag(dcx, level)
}
&AttributeLintKind::UnexpectedCfgName(name, value) => {
check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value)
.into_diag(dcx, level)
-18
View File
@@ -3399,24 +3399,6 @@ pub(crate) struct UnusedDuplicate {
)]
pub(crate) struct ExpectedNameValue;
#[derive(Diagnostic)]
#[diag("unsafe attribute used without unsafe")]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label("usage of unsafe attribute")]
pub span: Span,
#[subdiagnostic]
pub suggestion: Option<UnsafeAttrOutsideUnsafeSuggestion>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")]
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = "unsafe(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag("doc alias is duplicated")]
pub(crate) struct DocAliasDuplicated {
-4
View File
@@ -682,10 +682,6 @@ pub enum AttributeLintKind {
target: &'static str,
target_span: Span,
},
UnsafeAttrOutsideUnsafe {
attribute_name_span: Span,
sugg_spans: Option<(Span, Span)>,
},
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
DuplicateDocAlias {