From 86c1ca0e5cdd9fa31e34dc4ef55c3b671372adf2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Mar 2026 15:03:46 +0100 Subject: [PATCH 1/2] Set up API to make it possible to pass closures instead of `AttributeLint`. The end goal being to completely remove `AttributeLint`. --- Cargo.lock | 1 + compiler/rustc_ast_lowering/src/lib.rs | 28 ++++++---- compiler/rustc_attr_parsing/src/context.rs | 6 +-- compiler/rustc_attr_parsing/src/errors.rs | 9 ++++ compiler/rustc_attr_parsing/src/interface.rs | 54 ++++++++++++++------ compiler/rustc_attr_parsing/src/lib.rs | 2 +- compiler/rustc_attr_parsing/src/safety.rs | 22 +++++--- compiler/rustc_errors/src/diagnostic.rs | 12 +++++ compiler/rustc_errors/src/lib.rs | 4 +- compiler/rustc_hir/Cargo.toml | 1 + compiler/rustc_hir/src/lints.rs | 30 +++++++++-- compiler/rustc_interface/src/passes.rs | 7 +++ compiler/rustc_lint/src/early/diagnostics.rs | 9 ---- compiler/rustc_lint/src/lints.rs | 18 ------- compiler/rustc_lint_defs/src/lib.rs | 4 -- 15 files changed, 133 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02da343c5c7f8..35fa21ee357ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3995,6 +3995,7 @@ dependencies = [ "rustc_ast_pretty", "rustc_data_structures", "rustc_error_messages", + "rustc_errors", "rustc_hashes", "rustc_hir_id", "rustc_index", diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5fcc8f0161194..3fde441e4e5df 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -40,7 +40,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; 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; @@ -51,7 +51,7 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle}; 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::{DefPathData, DisambiguatorState}; -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, @@ -1166,13 +1166,23 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { 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, + })); + } }, ) } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 259a73de59853..b206ceae47982 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,7 +16,6 @@ use rustc_session::Session; 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::*; @@ -63,6 +62,7 @@ use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, ParsedDescription, }; use crate::target_checking::AllowedTargets; +use crate::{AttributeParser, EmitAttribute}; type GroupType = LazyLock>; pub(super) struct GroupTypeInner { @@ -454,7 +454,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { ) { return; } - (self.emit_lint)(LintId::of(lint), span, kind); + (self.emit_lint)(LintId::of(lint), span, EmitAttribute::Static(kind)); } pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { @@ -720,7 +720,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, Span, AttributeLintKind), + pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, EmitAttribute), } /// Context given to every attribute parser during finalization. diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index d4236416dd6aa..27eb74f2d7537 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -50,3 +50,12 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard { #[label("always matches")] pub wildcard_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, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 7305c4b7c2fa8..27b7a3a213f9f 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -3,7 +3,8 @@ use std::convert::identity; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety}; -use rustc_errors::DiagCtxtHandle; +use rustc_data_structures::sync::{DynSend, DynSync}; +use rustc_errors::{Diag, DiagCtxtHandle, Level}; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; @@ -18,6 +19,15 @@ use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; 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> { @@ -116,13 +126,18 @@ impl<'sess> AttributeParser<'sess, Early> { target, OmitDoc::Skip, std::convert::identity, - |lint_id, span, kind| { - sess.psess.buffer_lint( - lint_id.lint, - span, - target_node_id, - BuiltinLintDiag::AttributeLint(kind), - ) + |lint_id, span, kind| match kind { + EmitAttribute::Static(attr_kind) => { + sess.psess.buffer_lint( + lint_id.lint, + span, + target_node_id, + BuiltinLintDiag::AttributeLint(attr_kind), + ); + } + EmitAttribute::Dynamic(callback) => { + sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback); + } }, ) } @@ -200,16 +215,21 @@ impl<'sess> AttributeParser<'sess, Early> { sess, stage: Early { emit_errors }, }; - let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| { - sess.psess.buffer_lint( - lint_id.lint, - span, - target_node_id, - BuiltinLintDiag::AttributeLint(kind), - ) + let mut emit_lint = |lint_id: LintId, span: Span, kind: EmitAttribute| match kind { + EmitAttribute::Static(attr_kind) => { + sess.psess.buffer_lint( + lint_id.lint, + span, + target_node_id, + BuiltinLintDiag::AttributeLint(attr_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(&attr_path, inner_span, safety, &mut emit_lint) + parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint); } let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { shared: SharedContext { @@ -266,7 +286,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, - mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind), + mut emit_lint: impl FnMut(LintId, Span, EmitAttribute), ) -> Vec { let mut attributes = Vec::new(); // We store the attributes we intend to discard at the end of this function in order to diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 93eb5a0c3ab73..019d2d8135b4d 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -112,5 +112,5 @@ pub use attributes::cfg::{ 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; diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs index 4cc703c5d0cc4..fa1212bdbbd90 100644 --- a/compiler/rustc_attr_parsing/src/safety.rs +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -1,13 +1,13 @@ use rustc_ast::Safety; +use rustc_errors::Diagnostic; use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP}; 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::context::Stage; -use crate::{AttributeParser, ShouldEmit}; +use crate::{AttributeParser, EmitAttribute, ShouldEmit, errors}; impl<'sess, S: Stage> AttributeParser<'sess, S> { pub fn check_attribute_safety( @@ -15,7 +15,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { attr_path: &AttrPath, attr_span: Span, attr_safety: Safety, - emit_lint: &mut impl FnMut(LintId, Span, AttributeLintKind), + emit_lint: &mut impl FnMut(LintId, Span, EmitAttribute), ) { if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { return; @@ -84,11 +84,17 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { emit_lint( LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE), path_span, - 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) + })), ) } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9525a45d55f1b..986a684acd09b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -129,6 +129,18 @@ impl<'a> Diagnostic<'a, ()> } } +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)>(pub F); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index d17a4d6de42f5..8651f58e0cfa5 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -36,8 +36,8 @@ pub use anstyle::{ 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, diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 13e73acf07375..ee0a30f646e54 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -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" } diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 1589a6de220e7..c8cad52e0d784 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,4 +1,6 @@ use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::sync::{DynSend, DynSync}; +use rustc_errors::{Diag, DiagCtxtHandle, Level}; use rustc_lint_defs::LintId; pub use rustc_lint_defs::{AttributeLintKind, FormatWarning}; use rustc_macros::HashStable_Generic; @@ -21,13 +23,35 @@ pub struct DelayedLints { /// AST lowering to be emitted once HIR is built. #[derive(Debug, HashStable_Generic)] pub enum DelayedLint { - AttributeParsing(AttributeLint), + AttributeParsing(AttributeLint), + Dynamic(DynAttribute), } #[derive(Debug, HashStable_Generic)] -pub struct AttributeLint { +pub struct AttributeLint { pub lint_id: LintId, - pub id: Id, + pub id: HirId, pub span: Span, pub kind: AttributeLintKind, } + +#[derive(HashStable_Generic)] +pub struct DynAttribute { + pub lint_id: LintId, + pub id: HirId, + pub span: Span, + #[stable_hasher(ignore)] + 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() + } +} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d7d4f00578d1d..bfe72798150e5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -13,6 +13,7 @@ use rustc_data_structures::indexmap::IndexMap; 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; @@ -1045,6 +1046,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, + DiagCallback(&attribute_lint.callback), + ), } } } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 1cb0f906f730b..a20fc73544d3d 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -218,15 +218,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { 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) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index f89d9e51af917..3cb0b70853937 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3472,24 +3472,6 @@ pub(crate) struct ExpectedNoArgs; )] 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, -} - -#[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 { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index af1d1854fa5a0..8f0835c150a48 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -722,10 +722,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 { From ba032eea26d3352a8692f17d2bdb9f55f532bdc0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 26 Mar 2026 18:21:12 +0100 Subject: [PATCH 2/2] Remove `AttributeLintKind::UnusedDuplicate` --- .../rustc_attr_parsing/src/attributes/doc.rs | 15 +++--- compiler/rustc_attr_parsing/src/context.rs | 49 ++++++++++++++----- compiler/rustc_errors/src/lib.rs | 1 + compiler/rustc_errors/src/lints.rs | 15 ++++++ compiler/rustc_lint/src/early/diagnostics.rs | 3 -- compiler/rustc_lint/src/lints.rs | 13 ----- compiler/rustc_lint_defs/src/lib.rs | 5 -- 7 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 compiler/rustc_errors/src/lints.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 099a75e11f3af..e3bd2a20892d7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -1,5 +1,5 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; -use rustc_errors::msg; +use rustc_errors::{Diagnostic, msg}; use rustc_feature::template; use rustc_hir::Target; use rustc_hir::attrs::{ @@ -171,12 +171,15 @@ impl DocParser { if let Some(used_span) = self.attribute.no_crate_inject { let unused_span = path.span(); - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, + move |dcx, level| { + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + } + .into_diag(dcx, level) }, unused_span, ); diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b206ceae47982..80e5c7c6d0d60 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -6,7 +6,8 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; -use rustc_errors::{Diag, Diagnostic, Level}; +use rustc_data_structures::sync::{DynSend, DynSync}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; @@ -448,22 +449,43 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) { + self.emit_lint_inner(lint, EmitAttribute::Static(kind), span); + } + + /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing + /// must be delayed until after HIR is built. This method will take care of the details of + /// that. + pub(crate) fn emit_dyn_lint< + F: for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static, + >( + &mut self, + lint: &'static Lint, + callback: F, + span: Span, + ) { + self.emit_lint_inner(lint, EmitAttribute::Dynamic(Box::new(callback)), span); + } + + fn emit_lint_inner(&mut self, lint: &'static Lint, kind: EmitAttribute, span: Span) { if !matches!( self.stage.should_emit(), ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true } ) { return; } - (self.emit_lint)(LintId::of(lint), span, EmitAttribute::Static(kind)); + (self.emit_lint)(LintId::of(lint), span, kind); } pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { - self.emit_lint( + self.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: false, + move |dcx, level| { + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: false, + } + .into_diag(dcx, level) }, unused_span, ) @@ -474,12 +496,15 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { used_span: Span, unused_span: Span, ) { - self.emit_lint( + self.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, + move |dcx, level| { + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + } + .into_diag(dcx, level) }, unused_span, ) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8651f58e0cfa5..f4874652f6ace 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -77,6 +77,7 @@ mod diagnostic_impls; pub mod emitter; pub mod formatting; pub mod json; +pub mod lints; mod lock; pub mod markdown; pub mod timings; diff --git a/compiler/rustc_errors/src/lints.rs b/compiler/rustc_errors/src/lints.rs new file mode 100644 index 0000000000000..9c93a09bf764c --- /dev/null +++ b/compiler/rustc_errors/src/lints.rs @@ -0,0 +1,15 @@ +use rustc_macros::Diagnostic; +use rustc_span::Span; + +#[derive(Diagnostic)] +#[diag("unused attribute")] +pub struct UnusedDuplicate { + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + pub this: Span, + #[note("attribute also specified here")] + pub other: Span, + #[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] + pub warning: bool, +} diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index a20fc73544d3d..4de59c87e4306 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -172,9 +172,6 @@ pub struct DecorateAttrLint<'a, 'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - &AttributeLintKind::UnusedDuplicate { this, other, warning } => { - lints::UnusedDuplicate { this, other, warning }.into_diag(dcx, level) - } AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => { lints::IllFormedAttributeInput { num_suggestions: suggestions.len(), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 3cb0b70853937..4962452139321 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3438,19 +3438,6 @@ pub(crate) struct InvalidAttrStyle { pub target: &'static str, } -#[derive(Diagnostic)] -#[diag("unused attribute")] -pub(crate) struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] - pub this: Span, - #[note("attribute also specified here")] - pub other: Span, - #[warning( - "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" - )] - pub warning: bool, -} - #[derive(Diagnostic)] #[diag("malformed `doc` attribute input")] #[warning( diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 8f0835c150a48..7da654373c514 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -695,11 +695,6 @@ pub enum BuiltinLintDiag { #[derive(Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { - this: Span, - other: Span, - warning: bool, - }, IllFormedAttributeInput { suggestions: Vec, docs: Option<&'static str>,