From 80854e5bb692ef33cda138b13968c19af002c83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20D=C3=B6nszelmann?= Date: Thu, 6 Feb 2025 16:34:10 +0100 Subject: [PATCH 1/5] introduce duplicate attribute diagnostic logic --- .../src/attributes/deprecation.rs | 9 +- .../rustc_attr_parsing/src/attributes/mod.rs | 103 ++++++++++++++++-- .../src/attributes/stability.rs | 5 +- .../src/attributes/transparency.rs | 9 +- 4 files changed, 108 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 7d1417446b21..20a0fa85e7d3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -2,8 +2,8 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, sym}; -use super::SingleAttributeParser; use super::util::parse_version; +use super::{AttributeDuplicates, SingleAttributeParser}; use crate::context::AcceptContext; use crate::parser::ArgParser; use crate::session_diagnostics; @@ -47,12 +47,13 @@ fn get( impl SingleAttributeParser for DeprecationParser { const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; + const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) { + fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) { // FIXME(jdonszelmann): merge with errors from check_attrs.rs cx.emit_err(session_diagnostics::UnusedMultiple { - this: cx.attr_span, - other: first_span, + this: used, + other: unused, name: sym::deprecated, }); } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 6ecd6b4d7dbb..69d750107764 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,11 +17,12 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; use crate::context::{AcceptContext, FinalizeContext}; use crate::parser::ArgParser; +use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; @@ -74,11 +75,23 @@ pub(crate) trait AttributeParser: Default + 'static { pub(crate) trait SingleAttributeParser: 'static { const PATH: &'static [rustc_span::Symbol]; + const ON_DUPLICATE_STRATEGY: AttributeDuplicates; + /// Caled when a duplicate attribute is found. /// - /// `first_span` is the span of the first occurrence of this attribute. + /// - `unused` is the span of the attribute that was unused or bad because of some + /// duplicate reason (see [`AttributeDuplicates`]) + /// - `used` is the span of the attribute that was used in favor of the unused attribute // FIXME(jdonszelmann): default error - fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span); + fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) { + cx.emit_err(UnusedMultiple { + this: used, + other: unused, + name: Symbol::intern( + &Self::PATH.into_iter().map(|i| i.to_string()).collect::>().join(".."), + ), + }); + } /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option; @@ -94,12 +107,24 @@ impl Default for Single { impl AttributeParser for Single { const ATTRIBUTES: AcceptMapping = &[(T::PATH, |group: &mut Single, cx, args| { - if let Some((_, s)) = group.1 { - T::on_duplicate(cx, s); - return; - } - if let Some(pa) = T::convert(cx, args) { + match T::ON_DUPLICATE_STRATEGY { + // keep the first and error + AttributeDuplicates::ErrorFollowing => { + if let Some((_, unused)) = group.1 { + T::on_duplicate(cx, cx.attr_span, unused); + return; + } + } + // keep the new one and warn about the previous, + // then replace + AttributeDuplicates::FutureWarnPreceding => { + if let Some((_, used)) = group.1 { + T::on_duplicate(cx, used, cx.attr_span); + } + } + } + group.1 = Some((pa, cx.attr_span)); } })]; @@ -109,6 +134,68 @@ impl AttributeParser for Single { } } +pub(crate) enum OnDuplicate { + /// Give a default warning + Warn, + + /// Duplicates will be a warning, with a note that this will be an error in the future. + WarnButFutureError, + + /// Give a default error + Error, + + /// Ignore duplicates + Ignore, + + /// Custom function called when a duplicate attribute is found. + /// + /// - `unused` is the span of the attribute that was unused or bad because of some + /// duplicate reason (see [`AttributeDuplicates`]) + /// - `used` is the span of the attribute that was used in favor of the unused attribute + Custom(fn(cx: &AcceptContext<'_>, used: Span, unused: Span)), +} + +impl OnDuplicate { + fn exec(&self, cx: &AcceptContext<'_>, used: Span, unused: Span) { + match self { + OnDuplicate::Warn => { + todo!() + } + OnDuplicate::WarnButFutureError => { + todo!() + } + OnDuplicate::Error => { + cx.emit_err(UnusedMultiple { + this: used, + other: unused, + name: Symbol::intern( + &P::PATH.into_iter().map(|i| i.to_string()).collect::>().join(".."), + ), + }); + } + OnDuplicate::Ignore => {} + OnDuplicate::Custom(f) => f(cx, used, unused), + } + } +} + +pub(crate) enum AttributeDuplicates { + /// Duplicates after the first attribute will be an error. + /// + /// This should be used where duplicates would be ignored, but carry extra + /// meaning that could cause confusion. For example, `#[stable(since="1.0")] + /// #[stable(since="2.0")]`, which version should be used for `stable`? + ErrorFollowing, + + /// Duplicates preceding the last instance of the attribute will be a + /// warning, with a note that this will be an error in the future. + /// + /// This is the same as `FutureWarnFollowing`, except the last attribute is + /// the one that is "used". Ideally these can eventually migrate to + /// `ErrorPreceding`. + FutureWarnPreceding, +} + type ConvertFn = fn(ThinVec) -> AttributeKind; /// Alternative to [`AttributeParser`] that automatically handles state management. diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6d76456e83c8..a07f342a2e29 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -8,7 +8,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_span::{Span, Symbol, kw, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeParser, SingleAttributeParser}; +use super::{AcceptMapping, AttributeDuplicates, AttributeParser, SingleAttributeParser}; use crate::context::{AcceptContext, FinalizeContext}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -122,9 +122,10 @@ pub(crate) struct ConstStabilityIndirectParser; // FIXME(jdonszelmann): single word attribute group when we have these impl SingleAttributeParser for ConstStabilityIndirectParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; + const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; // ignore - fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {} + fn on_duplicate(_cx: &AcceptContext<'_>, _used: Span, _unused: Span) {} fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option { Some(AttributeKind::ConstStabilityIndirect) diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ad83a1f7af80..5d949c58e740 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,8 +1,8 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; -use rustc_span::sym; +use rustc_span::{Span, sym}; -use super::{AcceptContext, SingleAttributeParser}; +use super::{AcceptContext, AttributeDuplicates, SingleAttributeParser}; use crate::parser::ArgParser; pub(crate) struct TransparencyParser; @@ -12,9 +12,10 @@ pub(crate) struct TransparencyParser; #[allow(rustc::diagnostic_outside_of_impl)] impl SingleAttributeParser for TransparencyParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) { - cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); + fn on_duplicate(cx: &crate::context::AcceptContext<'_>, used: Span, unused: Span) { + cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); } fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { From 1170acb2792f7d649d92d8b4d3581524aba6a3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20D=C3=B6nszelmann?= Date: Thu, 6 Feb 2025 17:36:34 +0100 Subject: [PATCH 2/5] Start using new diagnostic logic on all existing `single` parsers --- .../src/attributes/deprecation.rs | 11 ++-------- .../rustc_attr_parsing/src/attributes/mod.rs | 21 +++---------------- .../src/attributes/stability.rs | 7 +++---- .../src/attributes/transparency.rs | 7 +++---- 4 files changed, 11 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 20a0fa85e7d3..4fc6f8b2b5c0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -2,6 +2,7 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, sym}; +use super::{AttributeDuplicates, OnDuplicate, SingleAttributeParser}; use super::util::parse_version; use super::{AttributeDuplicates, SingleAttributeParser}; use crate::context::AcceptContext; @@ -48,15 +49,7 @@ fn get( impl SingleAttributeParser for DeprecationParser { const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - - fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) { - // FIXME(jdonszelmann): merge with errors from check_attrs.rs - cx.emit_err(session_diagnostics::UnusedMultiple { - this: used, - other: unused, - name: sym::deprecated, - }); - } + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { let features = cx.features(); diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 69d750107764..ca7926d0cd1f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -76,22 +76,7 @@ pub(crate) trait SingleAttributeParser: 'static { const PATH: &'static [rustc_span::Symbol]; const ON_DUPLICATE_STRATEGY: AttributeDuplicates; - - /// Caled when a duplicate attribute is found. - /// - /// - `unused` is the span of the attribute that was unused or bad because of some - /// duplicate reason (see [`AttributeDuplicates`]) - /// - `used` is the span of the attribute that was used in favor of the unused attribute - // FIXME(jdonszelmann): default error - fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) { - cx.emit_err(UnusedMultiple { - this: used, - other: unused, - name: Symbol::intern( - &Self::PATH.into_iter().map(|i| i.to_string()).collect::>().join(".."), - ), - }); - } + const ON_DUPLICATE: OnDuplicate; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option; @@ -112,7 +97,7 @@ impl AttributeParser for Single { // keep the first and error AttributeDuplicates::ErrorFollowing => { if let Some((_, unused)) = group.1 { - T::on_duplicate(cx, cx.attr_span, unused); + T::ON_DUPLICATE.exec::(cx, cx.attr_span, unused); return; } } @@ -120,7 +105,7 @@ impl AttributeParser for Single { // then replace AttributeDuplicates::FutureWarnPreceding => { if let Some((_, used)) = group.1 { - T::on_duplicate(cx, used, cx.attr_span); + T::ON_DUPLICATE.exec::(cx, used, cx.attr_span); } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index a07f342a2e29..b3b39c85f3e1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -8,7 +8,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_span::{Span, Symbol, kw, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeDuplicates, AttributeParser, SingleAttributeParser}; +use super::{AcceptMapping, AttributeDuplicates, AttributeParser, OnDuplicate, SingleAttributeParser}; use crate::context::{AcceptContext, FinalizeContext}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -123,13 +123,12 @@ pub(crate) struct ConstStabilityIndirectParser; impl SingleAttributeParser for ConstStabilityIndirectParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - - // ignore - fn on_duplicate(_cx: &AcceptContext<'_>, _used: Span, _unused: Span) {} + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option { Some(AttributeKind::ConstStabilityIndirect) } + } #[derive(Default)] diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 5d949c58e740..9918fbe09132 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -2,7 +2,7 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; use rustc_span::{Span, sym}; -use super::{AcceptContext, AttributeDuplicates, SingleAttributeParser}; +use super::{AcceptContext, AttributeDuplicates, OnDuplicate, SingleAttributeParser}; use crate::parser::ArgParser; pub(crate) struct TransparencyParser; @@ -13,10 +13,9 @@ pub(crate) struct TransparencyParser; impl SingleAttributeParser for TransparencyParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, used: Span, unused: Span) { + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); - } + }); fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { match args.name_value().and_then(|nv| nv.value_as_str()) { From 49443bf6c15855b3627e46605d05d1a10a642201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 12 Feb 2025 13:59:08 +0100 Subject: [PATCH 3/5] lint on duplicates during attribute parsing To do this we stuff them in the diagnostic context to be emitted after hir is constructed --- compiler/rustc_ast_lowering/src/expr.rs | 9 +- compiler/rustc_ast_lowering/src/item.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 53 +++- compiler/rustc_attr_parsing/messages.ftl | 10 + .../src/attributes/allow_unstable.rs | 34 +-- .../src/attributes/confusables.rs | 8 +- .../src/attributes/deprecation.rs | 17 +- .../rustc_attr_parsing/src/attributes/mod.rs | 141 +++++---- .../rustc_attr_parsing/src/attributes/repr.rs | 21 +- .../src/attributes/stability.rs | 47 ++- .../src/attributes/transparency.rs | 13 +- compiler/rustc_attr_parsing/src/context.rs | 267 ++++++++++++++---- compiler/rustc_attr_parsing/src/lib.rs | 2 +- compiler/rustc_attr_parsing/src/parser.rs | 14 +- .../src/session_diagnostics.rs | 13 +- .../src/deriving/generic/mod.rs | 2 +- compiler/rustc_errors/src/lib.rs | 34 +++ compiler/rustc_interface/src/passes.rs | 7 + compiler/rustc_lint/src/nonstandard_style.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 3 +- 20 files changed, 482 insertions(+), 217 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index acf35b75e4df..69b887c96eb6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -74,12 +74,13 @@ impl<'hir> LoweringContext<'_, 'hir> { // Merge attributes into the inner expression. if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); + let new_attrs = self.lower_attrs_vec(&e.attrs, e.span, ex.hir_id) + .into_iter() + .chain(old_attrs.iter().cloned()); self.attrs.insert( ex.hir_id.local_id, &*self.arena.alloc_from_iter( - self.lower_attrs_vec(&e.attrs, e.span) - .into_iter() - .chain(old_attrs.iter().cloned()), + new_attrs, ), ); } @@ -2025,7 +2026,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ret_expr = self.checked_return(Some(from_residual_expr)); self.arena.alloc(self.expr(try_span, ret_expr)) }; - self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span); + self.lower_attrs(ret_expr.hir_id, &attrs, span); let break_pat = self.pat_cf_break(try_span, residual_local); self.arm(break_pat, ret_expr) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5e89321e6ecc..6bd48a78ebfc 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -58,7 +58,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { &mut self, owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, - ) { + ) { let mut lctx = LoweringContext::new(self.tcx, self.resolver); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4cecc56909e5..4ade110a708c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,7 +51,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; -use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{ self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate, }; @@ -188,7 +188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), - attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools), + attribute_parser: AttributeParser::new(tcx, tcx.features(), registered_tools), } } @@ -197,6 +197,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } +struct SpanLowerer { + is_incremental: bool, + defid: LocalDefId, +} + +impl SpanLowerer { + fn lower(&self, span: Span) -> Span { + if self.is_incremental { + span.with_parent(Some(self.defid)) + } else { + // Do not make spans relative when not using incremental compilation. + span + } + } +} + #[extension(trait ResolverAstLoweringExt)] impl ResolverAstLowering { fn legacy_const_generic_args(&self, expr: &Expr) -> Option> { @@ -740,15 +756,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } + fn span_lowerer(&self) -> SpanLowerer { + SpanLowerer { + is_incremental: self.tcx.sess.opts.incremental.is_some(), + defid: self.current_hir_id_owner.def_id, + } + } + /// Intercept all spans entering HIR. /// Mark a span as relative to the current owning item. fn lower_span(&self, span: Span) -> Span { - if self.tcx.sess.opts.incremental.is_some() { - span.with_parent(Some(self.current_hir_id_owner.def_id)) - } else { - // Do not make spans relative when not using incremental compilation. - span - } + self.span_lowerer().lower(span) } fn lower_ident(&self, ident: Ident) -> Ident { @@ -871,7 +889,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if attrs.is_empty() { &[] } else { - let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span)); + let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id); debug_assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(lowered_attrs); @@ -891,9 +909,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec { - self.attribute_parser - .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s)) + fn lower_attrs_vec( + &mut self, + attrs: &[Attribute], + target_span: Span, + target_hir_id: HirId, + ) -> Vec { + let l = self.span_lowerer(); + self.attribute_parser.parse_attribute_list( + attrs, + target_span, + target_hir_id, + OmitDoc::Lower, + |s| l.lower(s), + ) } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 45174c9582d3..573854afa746 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -131,7 +131,17 @@ attr_parsing_unsupported_literal_generic = attr_parsing_unsupported_literal_suggestion = consider removing the prefix +attr_parsing_unused_duplicate = + unused attribute + .suggestion = remove this attribute + .note = attribute also specified here + .warn = {-attr_parsing_previously_accepted} + + attr_parsing_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here + +-attr_parsing_perviously_accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index d37ede86cfd2..647ebd21b47c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -4,41 +4,43 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::{Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; -use crate::context::AcceptContext; +use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; -impl CombineAttributeParser for AllowInternalUnstableParser { +impl CombineAttributeParser for AllowInternalUnstableParser { const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator + 'a { - parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span)) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator { + parse_unstable(cx, args, >::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) } } pub(crate) struct AllowConstFnUnstableParser; -impl CombineAttributeParser for AllowConstFnUnstableParser { +impl CombineAttributeParser for AllowConstFnUnstableParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator + 'a { - parse_unstable(cx, args, Self::PATH[0]) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { + parse_unstable(cx, args, >::PATH[0]) } } -fn parse_unstable<'a>( - cx: &AcceptContext<'_>, - args: &'a ArgParser<'a>, +fn parse_unstable( + cx: &AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, symbol: Symbol, ) -> impl IntoIterator { let mut res = Vec::new(); diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 6cff952fcf22..afd3c012f05a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -3,7 +3,7 @@ use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; use super::{AcceptMapping, AttributeParser}; -use crate::context::FinalizeContext; +use crate::context::{FinalizeContext, Stage}; use crate::session_diagnostics; #[derive(Default)] @@ -12,8 +12,8 @@ pub(crate) struct ConfusablesParser { first_span: Option, } -impl AttributeParser for ConfusablesParser { - const ATTRIBUTES: AcceptMapping = &[(&[sym::rustc_confusables], |this, cx, args| { +impl AttributeParser for ConfusablesParser { + const ATTRIBUTES: AcceptMapping = &[(&[sym::rustc_confusables], |this, cx, args| { let Some(list) = args.list() else { // FIXME(jdonszelmann): error when not a list? Bring validation code here. // NOTE: currently subsequent attributes are silently ignored using @@ -45,7 +45,7 @@ impl AttributeParser for ConfusablesParser { this.first_span.get_or_insert(cx.attr_span); })]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { if self.confusables.is_empty() { return None; } diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 4fc6f8b2b5c0..71916e2b432e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -2,18 +2,17 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, sym}; -use super::{AttributeDuplicates, OnDuplicate, SingleAttributeParser}; use super::util::parse_version; -use super::{AttributeDuplicates, SingleAttributeParser}; -use crate::context::AcceptContext; +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; use crate::session_diagnostics::UnsupportedLiteralReason; pub(crate) struct DeprecationParser; -fn get( - cx: &AcceptContext<'_>, +fn get( + cx: &AcceptContext<'_, '_, S>, ident: Ident, param_span: Span, arg: &ArgParser<'_>, @@ -46,12 +45,12 @@ fn get( } } -impl SingleAttributeParser for DeprecationParser { +impl SingleAttributeParser for DeprecationParser { const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; - const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let features = cx.features(); let mut since = None; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ca7926d0cd1f..62ea87dfe4f6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,12 +17,13 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; +use rustc_session::lint::builtin::UNUSED_ATTRIBUTES; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, FinalizeContext}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; -use crate::session_diagnostics::UnusedMultiple; +use crate::session_diagnostics::{self, UnusedMultiple}; pub(crate) mod allow_unstable; pub(crate) mod cfg; @@ -33,8 +34,8 @@ pub(crate) mod stability; pub(crate) mod transparency; pub(crate) mod util; -type AcceptFn = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); -type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; +type AcceptFn = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>); +type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -52,15 +53,15 @@ type AcceptMapping = &'static [(&'static [rustc_span::Symbol], AcceptFn)]; /// whether it has seen the attribute it has been looking for. /// /// The state machine is automatically reset to parse attributes on the next item. -pub(crate) trait AttributeParser: Default + 'static { +pub(crate) trait AttributeParser: Default + 'static { /// The symbols for the attributes that this parser is interested in. /// /// If an attribute has this symbol, the `accept` function will be called on it. - const ATTRIBUTES: AcceptMapping; + const ATTRIBUTES: AcceptMapping; /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. - fn finalize(self, cx: &FinalizeContext<'_>) -> Option; + fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option; } /// Alternative to [`AttributeParser`] that automatically handles state management. @@ -72,54 +73,60 @@ pub(crate) trait AttributeParser: Default + 'static { /// /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. -pub(crate) trait SingleAttributeParser: 'static { +pub(crate) trait SingleAttributeParser: 'static { const PATH: &'static [rustc_span::Symbol]; - - const ON_DUPLICATE_STRATEGY: AttributeDuplicates; - const ON_DUPLICATE: OnDuplicate; + const ATTRIBUTE_ORDER: AttributeOrder; + const ON_DUPLICATE: OnDuplicate; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option; + fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option; } -pub(crate) struct Single(PhantomData, Option<(AttributeKind, Span)>); +pub(crate) struct Single, S: Stage>( + PhantomData<(S, T)>, + Option<(AttributeKind, Span)>, +); -impl Default for Single { +impl, S: Stage> Default for Single { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl AttributeParser for Single { - const ATTRIBUTES: AcceptMapping = &[(T::PATH, |group: &mut Single, cx, args| { - if let Some(pa) = T::convert(cx, args) { - match T::ON_DUPLICATE_STRATEGY { - // keep the first and error - AttributeDuplicates::ErrorFollowing => { - if let Some((_, unused)) = group.1 { - T::ON_DUPLICATE.exec::(cx, cx.attr_span, unused); - return; +impl, S: Stage> AttributeParser for Single { + const ATTRIBUTES: AcceptMapping = + &[(T::PATH, |group: &mut Single, cx, args| { + if let Some(pa) = T::convert(cx, args) { + match T::ATTRIBUTE_ORDER { + // keep the first and report immediately. ignore this attribute + AttributeOrder::KeepFirst => { + if let Some((_, unused)) = group.1 { + T::ON_DUPLICATE.exec::(cx, cx.attr_span, unused); + return; + } } - } - // keep the new one and warn about the previous, - // then replace - AttributeDuplicates::FutureWarnPreceding => { - if let Some((_, used)) = group.1 { - T::ON_DUPLICATE.exec::(cx, used, cx.attr_span); + // keep the new one and warn about the previous, + // then replace + AttributeOrder::KeepLast => { + if let Some((_, used)) = group.1 { + T::ON_DUPLICATE.exec::(cx, used, cx.attr_span); + } } } - } - group.1 = Some((pa, cx.attr_span)); - } - })]; + group.1 = Some((pa, cx.attr_span)); + } + })]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { Some(self.1?.0) } } -pub(crate) enum OnDuplicate { +// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing +// them will be merged in another PR +#[allow(unused)] +pub(crate) enum OnDuplicate { /// Give a default warning Warn, @@ -137,18 +144,27 @@ pub(crate) enum OnDuplicate { /// - `unused` is the span of the attribute that was unused or bad because of some /// duplicate reason (see [`AttributeDuplicates`]) /// - `used` is the span of the attribute that was used in favor of the unused attribute - Custom(fn(cx: &AcceptContext<'_>, used: Span, unused: Span)), + Custom(fn(cx: &AcceptContext<'_, '_, S>, used: Span, unused: Span)), } -impl OnDuplicate { - fn exec(&self, cx: &AcceptContext<'_>, used: Span, unused: Span) { +impl OnDuplicate { + fn exec>( + &self, + cx: &mut AcceptContext<'_, '_, S>, + used: Span, + unused: Span, + ) { match self { - OnDuplicate::Warn => { - todo!() - } - OnDuplicate::WarnButFutureError => { - todo!() - } + OnDuplicate::Warn => cx.emit_lint( + UNUSED_ATTRIBUTES, + unused, + session_diagnostics::UnusedDuplicate { this: unused, other: used, warning: false }, + ), + OnDuplicate::WarnButFutureError => cx.emit_lint( + UNUSED_ATTRIBUTES, + unused, + session_diagnostics::UnusedDuplicate { this: unused, other: used, warning: true }, + ), OnDuplicate::Error => { cx.emit_err(UnusedMultiple { this: used, @@ -163,14 +179,17 @@ impl OnDuplicate { } } } - -pub(crate) enum AttributeDuplicates { +// +// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing +// them will be merged in another PR +#[allow(unused)] +pub(crate) enum AttributeOrder { /// Duplicates after the first attribute will be an error. /// /// This should be used where duplicates would be ignored, but carry extra /// meaning that could cause confusion. For example, `#[stable(since="1.0")] /// #[stable(since="2.0")]`, which version should be used for `stable`? - ErrorFollowing, + KeepFirst, /// Duplicates preceding the last instance of the attribute will be a /// warning, with a note that this will be an error in the future. @@ -178,7 +197,7 @@ pub(crate) enum AttributeDuplicates { /// This is the same as `FutureWarnFollowing`, except the last attribute is /// the one that is "used". Ideally these can eventually migrate to /// `ErrorPreceding`. - FutureWarnPreceding, + KeepLast, } type ConvertFn = fn(ThinVec) -> AttributeKind; @@ -190,35 +209,35 @@ type ConvertFn = fn(ThinVec) -> AttributeKind; /// /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. -pub(crate) trait CombineAttributeParser: 'static { +pub(crate) trait CombineAttributeParser: 'static { const PATH: &'static [rustc_span::Symbol]; type Item; const CONVERT: ConvertFn; /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator + 'a; + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c; } -pub(crate) struct Combine( - PhantomData, - ThinVec<::Item>, +pub(crate) struct Combine, S: Stage>( + PhantomData<(S, T)>, + ThinVec<>::Item>, ); -impl Default for Combine { +impl, S: Stage> Default for Combine { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl AttributeParser for Combine { - const ATTRIBUTES: AcceptMapping = - &[(T::PATH, |group: &mut Combine, cx, args| group.1.extend(T::extend(cx, args)))]; +impl, S: Stage> AttributeParser for Combine { + const ATTRIBUTES: AcceptMapping = + &[(T::PATH, |group: &mut Combine, cx, args| group.1.extend(T::extend(cx, args)))]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 26ca637faec6..1ee4359dcdac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -4,7 +4,7 @@ use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; use rustc_span::{Span, Symbol, sym}; use super::{CombineAttributeParser, ConvertFn}; -use crate::context::AcceptContext; +use crate::context::{AcceptContext, Stage}; use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser}; use crate::session_diagnostics; use crate::session_diagnostics::IncorrectReprFormatGenericCause; @@ -19,15 +19,15 @@ use crate::session_diagnostics::IncorrectReprFormatGenericCause; // FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct? pub(crate) struct ReprParser; -impl CombineAttributeParser for ReprParser { +impl CombineAttributeParser for ReprParser { type Item = (ReprAttr, Span); const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; const CONVERT: ConvertFn = AttributeKind::Repr; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator + 'a { + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { let mut reprs = Vec::new(); let Some(list) = args.list() else { @@ -91,7 +91,10 @@ fn int_type_of_word(s: Symbol) -> Option { } } -fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option { +fn parse_repr( + cx: &AcceptContext<'_, '_, S>, + param: &MetaItemParser<'_>, +) -> Option { use ReprAttr::*; // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the @@ -163,8 +166,8 @@ enum AlignKind { Align, } -fn parse_repr_align( - cx: &AcceptContext<'_>, +fn parse_repr_align( + cx: &AcceptContext<'_, '_, S>, list: &MetaItemListParser<'_>, param_span: Span, align_kind: AlignKind, diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index b3b39c85f3e1..00b9e23d3a73 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -8,8 +8,8 @@ use rustc_errors::ErrorGuaranteed; use rustc_span::{Span, Symbol, kw, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeDuplicates, AttributeParser, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, FinalizeContext}; +use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -31,7 +31,7 @@ pub(crate) struct StabilityParser { impl StabilityParser { /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. - fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool { + fn check_duplicate(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -41,8 +41,8 @@ impl StabilityParser { } } -impl AttributeParser for StabilityParser { - const ATTRIBUTES: AcceptMapping = &[ +impl AttributeParser for StabilityParser { + const ATTRIBUTES: AcceptMapping = &[ (&[sym::stable], |this, cx, args| { reject_outside_std!(cx); if !this.check_duplicate(cx) @@ -69,7 +69,7 @@ impl AttributeParser for StabilityParser { }), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { if let Some(atum) = self.allowed_through_unstable_modules { if let Some(( Stability { @@ -99,8 +99,8 @@ pub(crate) struct BodyStabilityParser { stability: Option<(DefaultBodyStability, Span)>, } -impl AttributeParser for BodyStabilityParser { - const ATTRIBUTES: AcceptMapping = +impl AttributeParser for BodyStabilityParser { + const ATTRIBUTES: AcceptMapping = &[(&[sym::rustc_default_body_unstable], |this, cx, args| { reject_outside_std!(cx); if this.stability.is_some() { @@ -111,7 +111,7 @@ impl AttributeParser for BodyStabilityParser { } })]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { let (stability, span) = self.stability?; Some(AttributeKind::BodyStability { stability, span }) @@ -120,15 +120,14 @@ impl AttributeParser for BodyStabilityParser { pub(crate) struct ConstStabilityIndirectParser; // FIXME(jdonszelmann): single word attribute group when we have these -impl SingleAttributeParser for ConstStabilityIndirectParser { +impl SingleAttributeParser for ConstStabilityIndirectParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; - const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option { + fn convert(_cx: &AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { Some(AttributeKind::ConstStabilityIndirect) } - } #[derive(Default)] @@ -139,7 +138,7 @@ pub(crate) struct ConstStabilityParser { impl ConstStabilityParser { /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. - fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool { + fn check_duplicate(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -149,8 +148,8 @@ impl ConstStabilityParser { } } -impl AttributeParser for ConstStabilityParser { - const ATTRIBUTES: AcceptMapping = &[ +impl AttributeParser for ConstStabilityParser { + const ATTRIBUTES: AcceptMapping = &[ (&[sym::rustc_const_stable], |this, cx, args| { reject_outside_std!(cx); @@ -180,7 +179,7 @@ impl AttributeParser for ConstStabilityParser { }), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { if self.promotable { if let Some((ref mut stab, _)) = self.stability { stab.promotable = true; @@ -200,8 +199,8 @@ impl AttributeParser for ConstStabilityParser { /// /// Emits an error when either the option was already Some, or the arguments weren't of form /// `name = value` -fn insert_value_into_option_or_error( - cx: &AcceptContext<'_>, +fn insert_value_into_option_or_error( + cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser<'_>, item: &mut Option, ) -> Option<()> { @@ -227,8 +226,8 @@ fn insert_value_into_option_or_error( /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and /// its stability information. -pub(crate) fn parse_stability( - cx: &AcceptContext<'_>, +pub(crate) fn parse_stability( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -293,8 +292,8 @@ pub(crate) fn parse_stability( // Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` /// attribute, and return the feature name and its stability information. -pub(crate) fn parse_unstability( - cx: &AcceptContext<'_>, +pub(crate) fn parse_unstability( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 9918fbe09132..f3d4327d897e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -1,8 +1,9 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; -use rustc_span::{Span, sym}; +use rustc_span::sym; -use super::{AcceptContext, AttributeDuplicates, OnDuplicate, SingleAttributeParser}; +use super::{AcceptContext, AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::Stage; use crate::parser::ArgParser; pub(crate) struct TransparencyParser; @@ -10,14 +11,14 @@ pub(crate) struct TransparencyParser; // FIXME(jdonszelmann): make these proper diagnostics #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] -impl SingleAttributeParser for TransparencyParser { +impl SingleAttributeParser for TransparencyParser { const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; - const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { match args.name_value().and_then(|nv| nv.value_as_str()) { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semitransparent) => Some(Transparency::SemiTransparent), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0e6b0bab082e..7dd1127beb9f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,14 +1,18 @@ use std::cell::RefCell; use std::collections::BTreeMap; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use rustc_ast::{self as ast, DelimArgs}; +use private::Sealed; +use rustc_ast::{self as ast, DelimArgs, NodeId}; use rustc_attr_data_structures::AttributeKind; -use rustc_errors::{DiagCtxtHandle, Diagnostic}; +use rustc_data_structures::sync::DynSend; +use rustc_errors::{DiagCtxtHandle, Diagnostic, LintDiagnostic}; use rustc_feature::Features; -use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId}; +use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId}; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_session::lint::Lint; use rustc_span::symbol::kw; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; @@ -23,16 +27,40 @@ use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single}; use crate::parser::{ArgParser, MetaItemParser}; +macro_rules! group_type { + ($stage: ty) => { + LazyLock<( + BTreeMap<&'static [Symbol], Vec Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>>, + Vec) -> Option>> + )> + }; +} + macro_rules! attribute_groups { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - pub(crate) static $name: LazyLock<( - BTreeMap<&'static [Symbol], Vec, &ArgParser<'_>) + Send + Sync>>>, - Vec) -> Option>> - )> = LazyLock::new(|| { - let mut accepts = BTreeMap::<_, Vec, &ArgParser<'_>) + Send + Sync>>>::new(); - let mut finalizes = Vec::) -> Option>>::new(); + mod early { + use super::*; + type Combine = super::Combine; + type Single = super::Single; + + attribute_groups!(@[Early] pub(crate) static $name = [$($names),*];); + } + mod late { + use super::*; + type Combine = super::Combine; + type Single = super::Single; + + attribute_groups!(@[Late] pub(crate) static $name = [$($names),*];); + } + }; + ( + @[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?]; + ) => { + pub(crate) static $name: group_type!($ty) = LazyLock::new(|| { + let mut accepts = BTreeMap::<_, Vec Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>>::new(); + let mut finalizes = Vec::) -> Option>>::new(); $( { thread_local! { @@ -58,7 +86,6 @@ macro_rules! attribute_groups { }); }; } - attribute_groups!( pub(crate) static ATTRIBUTE_MAPPING = [ // tidy-alphabetical-start @@ -82,49 +109,165 @@ attribute_groups!( ]; ); +mod private { + pub trait Sealed {} + impl Sealed for super::Early {} + impl Sealed for super::Late {} +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +pub trait Stage: Sized + 'static + Sealed { + type Id: Copy; + type Sess<'sess>; + fn parsers() -> &'static group_type!(Self); + + fn sess<'short, 'sess: 'short>(s: &Self::Sess<'sess>) -> &'short Session; + fn emit_err<'sess>( + sess: &Self::Sess<'sess>, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed; + + fn emit_lint( + dcx: DiagCtxtHandle<'_>, + lint: &'static Lint, + id: Self::Id, + span: Span, + diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, + ); +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Early { + type Id = NodeId; + type Sess<'sess> = &'sess Session; + + fn parsers() -> &'static group_type!(Self) { + &early::ATTRIBUTE_MAPPING + } + fn sess<'short, 'sess: 'short>(s: &Self::Sess<'sess>) -> &'short Session { + s + } + fn emit_err<'sess>( + sess: &Self::Sess<'sess>, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { + sess.dcx().create_err(diag).delay_as_bug() + } + + fn emit_lint( + _dcx: DiagCtxtHandle<'_>, + _lint: &'static Lint, + _id: Self::Id, + _span: Span, + _diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, + ) { + // FIXME(jdonszelmann): currently not a thing (yet) + panic!("lints during ast attr parsing not yet implemented"); + } +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Late { + type Id = HirId; + type Sess<'sess> = TyCtxt<'sess>; + + fn parsers() -> &'static group_type!(Self) { + &late::ATTRIBUTE_MAPPING + } + fn sess<'short, 'sess: 'short>(tcx: &Self::Sess<'sess>) -> &'short Session { + &tcx.sess + } + fn emit_err<'sess>( + tcx: &Self::Sess<'sess>, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { + tcx.dcx().emit_err(diag) + } + + fn emit_lint( + dcx: DiagCtxtHandle<'_>, + lint: &'static Lint, + id: Self::Id, + span: Span, + diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, + ) { + println!("stashing lint"); + dcx.delay_lint_during_ast_lowering((lint, id, span, Box::new(|x| { + diag.decorate_lint(x) + }))); + } +} + +/// used when parsing attributes for miscelaneous things *before* ast lowering +pub struct Early; +/// used when parsing attributes during ast lowering +pub struct Late; + /// Context given to every attribute parser when accepting /// /// Gives [`AttributeParser`]s enough information to create errors, for example. -pub(crate) struct AcceptContext<'a> { - pub(crate) group_cx: &'a FinalizeContext<'a>, +pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { + pub(crate) group_cx: FinalizeContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, } -impl<'a> AcceptContext<'a> { - pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed { - if self.limit_diagnostics { - self.dcx().create_err(diag).delay_as_bug() - } else { - self.dcx().emit_err(diag) - } +impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { + pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + S::emit_err(&self.sess, diag) + } + + pub(crate) fn emit_lint( + & self, + lint: &'static Lint, + span: Span, + diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, + ) { + S::emit_lint(self.dcx(), lint, self.target_id, span, diag) } } -impl<'a> Deref for AcceptContext<'a> { - type Target = FinalizeContext<'a>; +impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { + type Target = FinalizeContext<'f, 'sess, S>; fn deref(&self) -> &Self::Target { &self.group_cx } } +impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.group_cx + } +} + /// Context given to every attribute parser during finalization. /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example. -pub(crate) struct FinalizeContext<'a> { +pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. - pub(crate) cx: &'a AttributeParser<'a>, + pub(crate) cx: &'p mut AttributeParser<'sess, S>, /// The span of the syntactical component this attribute was applied to pub(crate) target_span: Span, + /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to + pub(crate) target_id: S::Id, } -impl<'a> Deref for FinalizeContext<'a> { - type Target = AttributeParser<'a>; +impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { + type Target = AttributeParser<'sess, S>; fn deref(&self) -> &Self::Target { - &self.cx + self.cx + } +} + +impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.cx } } @@ -136,24 +279,20 @@ pub enum OmitDoc { /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. -pub struct AttributeParser<'sess> { +pub struct AttributeParser<'sess, S: Stage = Late> { #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes tools: Vec, - sess: &'sess Session, features: Option<&'sess Features>, + sess: S::Sess<'sess>, /// *only* parse attributes with this symbol. /// /// Used in cases where we want the lowering infrastructure for /// parse just a single attribute. parse_only: Option, - - /// Can be used to instruct parsers to reduce the number of diagnostics it emits. - /// Useful when using `parse_limited` and you know the attr will be reparsed later. - pub(crate) limit_diagnostics: bool, } -impl<'sess> AttributeParser<'sess> { +impl<'sess> AttributeParser<'sess, Early> { /// This method allows you to parse attributes *before* you have access to features or tools. /// One example where this is necessary, is to parse `feature` attributes themselves for /// example. @@ -164,33 +303,44 @@ impl<'sess> AttributeParser<'sess> { /// /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with /// that symbol are picked out of the list of instructions and parsed. Those are returned. + /// + /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while + /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed + /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], sym: Symbol, target_span: Span, - limit_diagnostics: bool, + target_node_id: NodeId, ) -> Option { - let mut parsed = Self { - sess, - features: None, - tools: Vec::new(), - parse_only: Some(sym), - limit_diagnostics, - } - .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity); - + let mut p = Self { features: None, tools: Vec::new(), parse_only: Some(sym), sess }; + let mut parsed = p.parse_attribute_list( + attrs, + target_span, + target_node_id, + OmitDoc::Skip, + std::convert::identity, + ); assert!(parsed.len() <= 1); parsed.pop() } - pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec) -> Self { - Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false } + pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec) -> Self { + Self { features: Some(features), tools, parse_only: None, sess } } +} + +impl<'sess> AttributeParser<'sess, Late> { + pub fn new(tcx: TyCtxt<'sess>, features: &'sess Features, tools: Vec) -> Self { + Self { features: Some(features), tools, parse_only: None, sess: tcx } + } +} +impl<'sess, S: Stage> AttributeParser<'sess, S> { pub(crate) fn sess(&self) -> &'sess Session { - self.sess + S::sess(&self.sess) } pub(crate) fn features(&self) -> &'sess Features { @@ -198,25 +348,24 @@ impl<'sess> AttributeParser<'sess> { } pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { - self.sess.dcx() + self.sess().dcx() } /// Parse a list of attributes. /// /// `target_span` is the span of the thing this list of attributes is applied to, /// and when `omit_doc` is set, doc attributes are filtered out. - pub fn parse_attribute_list<'a>( - &'a self, - attrs: &'a [ast::Attribute], + pub fn parse_attribute_list( + &mut self, + attrs: &[ast::Attribute], target_span: Span, + target_id: S::Id, omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, ) -> Vec { let mut attributes = Vec::new(); - let group_cx = FinalizeContext { cx: self, target_span }; - for attr in attrs { // if we're only looking for a single attribute, // skip all the ones we don't care about @@ -264,14 +413,14 @@ impl<'sess> AttributeParser<'sess> { let (path, args) = parser.deconstruct(); let parts = path.segments().map(|i| i.name).collect::>(); - if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { + if let Some(accepts) = S::parsers().0.get(parts.as_slice()) { for f in accepts { - let cx = AcceptContext { - group_cx: &group_cx, + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + group_cx: FinalizeContext { cx: self, target_span, target_id }, attr_span: lower_span(attr.span), }; - f(&cx, &args) + f(&mut cx, args) } } else { // if we're here, we must be compiling a tool attribute... Or someone forgot to @@ -302,8 +451,8 @@ impl<'sess> AttributeParser<'sess> { } let mut parsed_attributes = Vec::new(); - for f in &ATTRIBUTE_MAPPING.1 { - if let Some(attr) = f(&group_cx) { + for f in &S::parsers().1 { + if let Some(attr) = f(&mut FinalizeContext { cx: self, target_span, target_id }) { parsed_attributes.push(Attribute::Parsed(attr)); } } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 249e71ef70dc..90642e032d08 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -90,7 +90,7 @@ mod session_diagnostics; pub use attributes::cfg::*; pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version}; -pub use context::{AttributeParser, OmitDoc}; +pub use context::{AttributeParser, Early, Late, OmitDoc}; pub use rustc_attr_data_structures::*; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index f0cce26f4e24..08b99b14d529 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -116,7 +116,7 @@ impl<'a> ArgParser<'a> { } } - pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self { + pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self { match value { AttrArgs::Empty => Self::NoArgs, AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { @@ -236,7 +236,7 @@ impl<'a> Debug for MetaItemParser<'a> { impl<'a> MetaItemParser<'a> { /// Create a new parser from a [`NormalAttr`], which is stored inside of any /// [`ast::Attribute`](rustc_ast::Attribute) - pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self { + pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self { Self { path: PathParser::Ast(&attr.item.path), args: ArgParser::from_attr_args(&attr.item.args, dcx), @@ -364,13 +364,13 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit } } -struct MetaItemListParserContext<'a> { +struct MetaItemListParserContext<'a, 'sess> { // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside inside_delimiters: Peekable>, - dcx: DiagCtxtHandle<'a>, + dcx: DiagCtxtHandle<'sess>, } -impl<'a> MetaItemListParserContext<'a> { +impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { fn done(&mut self) -> bool { self.inside_delimiters.peek().is_none() } @@ -571,11 +571,11 @@ pub struct MetaItemListParser<'a> { } impl<'a> MetaItemListParser<'a> { - fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> { + fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self { MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx) } - fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self { + fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self { MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9d34b807ac2f..c185ec8a1079 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -3,7 +3,7 @@ use std::num::IntErrorKind; use rustc_ast as ast; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; @@ -451,6 +451,17 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } +#[derive(LintDiagnostic)] +#[diag(attr_parsing_unused_multiple)] +pub(crate) struct UnusedDuplicate { + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + #[warning] + pub warning: bool, +} + #[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 5402b5a1ae9f..8930f24800dc 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -482,7 +482,7 @@ impl<'a> TraitDef<'a> { match item { Annotatable::Item(item) => { let is_packed = matches!( - AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true), + AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 80e43ede4453..9cbe87047469 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -53,6 +53,8 @@ pub use diagnostic_impls::{ DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, IndicateAnonymousLifetime, SingleLabelManySpans, }; +use rustc_lint_defs::Lint; +use rustc_hir::HirId; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; use rustc_data_structures::AtomicRef; @@ -536,6 +538,13 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { } } +pub type HirDelayedLint = ( + &'static Lint, + HirId, + Span, + Box FnOnce(&'b mut Diag<'a, ()>) + 'static> +); + /// This inner struct exists to keep it all behind a single lock; /// this is done to prevent possible deadlocks in a multi-threaded compiler, /// as well as inconsistent state observation. @@ -552,6 +561,11 @@ struct DiagCtxtInner { /// The delayed bugs and their error guarantees. delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>, + /// *specifically* for lints emitted during ast lowering. + /// At this point we can't get the lint level yet (doing so will cause really weird bugs). + /// So, by stuffing lints in here they can be emitted when hir is built. + hir_delayed_lints: Vec, + /// The error count shown to the user at the end. deduplicated_err_count: usize, /// The warning count shown to the user at the end. @@ -769,6 +783,18 @@ impl DiagCtxt { inner.emitter = new_emitter; } + /// You can't emit lints during ast lowering, + /// since you'd need to find the lint level of hir nodes you're currently working on lowering. + /// + /// This stashes them and emits them during the late lint pass. + /// + /// Lints stashed at other stages of the compiler will be ignored. + /// Instead, in all other stages after hir lowering you should be able to + /// use `TyCtx::{emit}_node_span_lint(...)`. + pub fn delay_lint_during_ast_lowering(&self, lint: HirDelayedLint) { + self.inner.borrow_mut().hir_delayed_lints.push(lint); + } + pub fn set_emitter(&self, emitter: Box) { self.inner.borrow_mut().emitter = emitter; } @@ -828,6 +854,7 @@ impl DiagCtxt { future_breakage_diagnostics, fulfilled_expectations, ice_file: _, + hir_delayed_lints, } = inner.deref_mut(); // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the @@ -846,6 +873,7 @@ impl DiagCtxt { *stashed_diagnostics = Default::default(); *future_breakage_diagnostics = Default::default(); *fulfilled_expectations = Default::default(); + *hir_delayed_lints = Default::default(); } pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> { @@ -991,6 +1019,11 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow_mut().emit_stashed_diagnostics() } + /// Emit all lints that were delayed while building the hir. + pub fn steal_hir_delayed_lints(&self) -> Vec { + std::mem::take(&mut self.inner.borrow_mut().hir_delayed_lints) + } + /// This excludes delayed bugs. #[inline] pub fn err_count(&self) -> usize { @@ -1479,6 +1512,7 @@ impl DiagCtxtInner { future_breakage_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), ice_file: None, + hir_delayed_lints: Vec::new(), } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e47385d08994..e9f5e6792d6a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -882,6 +882,13 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { CStore::from_tcx(tcx).report_unused_deps(tcx); }, + { + // emit lints that couldnt be emitted during hir building because + // the lint level couldn't be determined + for (lint, hir_id, span, decorate) in tcx.dcx().steal_hir_delayed_lints() { + tcx.node_span_lint(lint, hir_id, span, decorate); + } + }, { tcx.par_hir_for_each_module(|module| { tcx.ensure_ok().check_mod_loops(module); diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 49f9ad39780a..1df8fd828667 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -163,7 +163,7 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = matches!( - AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true), + AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 33f529851ae4..77e224c46725 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -137,7 +137,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // FIXME(jdonszelmann) make one of these in the resolver? // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can. // Does that prevents errors from happening? maybe - let parser = AttributeParser::new( + let mut parser = AttributeParser::new_early( &self.resolver.tcx.sess, self.resolver.tcx.features(), Vec::new(), @@ -145,6 +145,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let attrs = parser.parse_attribute_list( &i.attrs, i.span, + i.id, OmitDoc::Skip, std::convert::identity, ); From 9aeead42d2955f68a19703c9b81db08d125a1983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 3 Mar 2025 17:45:14 +0100 Subject: [PATCH 4/5] add error message for unused duplicate --- compiler/rustc_ast_lowering/src/expr.rs | 15 ++++++--------- compiler/rustc_ast_lowering/src/item.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 2 +- compiler/rustc_attr_parsing/messages.ftl | 4 +--- compiler/rustc_attr_parsing/src/context.rs | 18 ++++++++---------- .../src/session_diagnostics.rs | 2 +- compiler/rustc_errors/src/lib.rs | 7 +++---- 7 files changed, 21 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 69b887c96eb6..8f7d9e9dd667 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -74,15 +74,12 @@ impl<'hir> LoweringContext<'_, 'hir> { // Merge attributes into the inner expression. if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); - let new_attrs = self.lower_attrs_vec(&e.attrs, e.span, ex.hir_id) - .into_iter() - .chain(old_attrs.iter().cloned()); - self.attrs.insert( - ex.hir_id.local_id, - &*self.arena.alloc_from_iter( - new_attrs, - ), - ); + let new_attrs = self + .lower_attrs_vec(&e.attrs, e.span, ex.hir_id) + .into_iter() + .chain(old_attrs.iter().cloned()); + self.attrs + .insert(ex.hir_id.local_id, &*self.arena.alloc_from_iter(new_attrs)); } return ex; } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 6bd48a78ebfc..5e89321e6ecc 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -58,7 +58,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { &mut self, owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, - ) { + ) { let mut lctx = LoweringContext::new(self.tcx, self.resolver); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4ade110a708c..7300816a3c01 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,7 +51,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate, }; diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 573854afa746..c9443feb021b 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -135,9 +135,7 @@ attr_parsing_unused_duplicate = unused attribute .suggestion = remove this attribute .note = attribute also specified here - .warn = {-attr_parsing_previously_accepted} - - + .warn = {-passes_previously_accepted} attr_parsing_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7dd1127beb9f..95aa22ca2798 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -195,9 +195,7 @@ impl Stage for Late { diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, ) { println!("stashing lint"); - dcx.delay_lint_during_ast_lowering((lint, id, span, Box::new(|x| { - diag.decorate_lint(x) - }))); + dcx.delay_lint_during_ast_lowering((lint, id, span, Box::new(|x| diag.decorate_lint(x)))); } } @@ -221,7 +219,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } pub(crate) fn emit_lint( - & self, + &self, lint: &'static Lint, span: Span, diag: impl DynSend + for<'x> LintDiagnostic<'x, ()> + 'static, @@ -316,12 +314,12 @@ impl<'sess> AttributeParser<'sess, Early> { ) -> Option { let mut p = Self { features: None, tools: Vec::new(), parse_only: Some(sym), sess }; let mut parsed = p.parse_attribute_list( - attrs, - target_span, - target_node_id, - OmitDoc::Skip, - std::convert::identity, - ); + attrs, + target_span, + target_node_id, + OmitDoc::Skip, + std::convert::identity, + ); assert!(parsed.len() <= 1); parsed.pop() diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index c185ec8a1079..8698cf0e5839 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -452,7 +452,7 @@ pub(crate) struct UnusedMultiple { } #[derive(LintDiagnostic)] -#[diag(attr_parsing_unused_multiple)] +#[diag(attr_parsing_unused_duplicate)] pub(crate) struct UnusedDuplicate { #[suggestion(code = "", applicability = "machine-applicable")] pub this: Span, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9cbe87047469..6aa47c4a84ca 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -53,8 +53,6 @@ pub use diagnostic_impls::{ DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, IndicateAnonymousLifetime, SingleLabelManySpans, }; -use rustc_lint_defs::Lint; -use rustc_hir::HirId; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; use rustc_data_structures::AtomicRef; @@ -66,8 +64,9 @@ pub use rustc_error_messages::{ SubdiagMessage, fallback_fluent_bundle, fluent_bundle, }; use rustc_hashes::Hash128; -use rustc_lint_defs::LintExpectationId; +use rustc_hir::HirId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; +use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; @@ -542,7 +541,7 @@ pub type HirDelayedLint = ( &'static Lint, HirId, Span, - Box FnOnce(&'b mut Diag<'a, ()>) + 'static> + Box FnOnce(&'b mut Diag<'a, ()>) + 'static>, ); /// This inner struct exists to keep it all behind a single lock; From fc6355157b941bb7ffb82da93eb7a8f2c01085ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 7 Mar 2025 15:34:14 +0100 Subject: [PATCH 5/5] remove 'static in some places --- .../rustc_attr_parsing/src/attributes/allow_unstable.rs | 4 ++-- compiler/rustc_attr_parsing/src/attributes/deprecation.rs | 2 +- compiler/rustc_attr_parsing/src/attributes/mod.rs | 8 ++++---- compiler/rustc_attr_parsing/src/attributes/repr.rs | 2 +- compiler/rustc_attr_parsing/src/attributes/stability.rs | 2 +- .../rustc_attr_parsing/src/attributes/transparency.rs | 2 +- compiler/rustc_attr_parsing/src/lib.rs | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 647ebd21b47c..03d08c0424d7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -10,7 +10,7 @@ use crate::session_diagnostics; pub(crate) struct AllowInternalUnstableParser; impl CombineAttributeParser for AllowInternalUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; + const PATH: &[rustc_span::Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; @@ -26,7 +26,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser { pub(crate) struct AllowConstFnUnstableParser; impl CombineAttributeParser for AllowConstFnUnstableParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; + const PATH: &[rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 71916e2b432e..7c34090e91cb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -46,7 +46,7 @@ fn get( } impl SingleAttributeParser for DeprecationParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; + const PATH: &[rustc_span::Symbol] = &[sym::deprecated]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 62ea87dfe4f6..148e4ab19a9a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,7 +12,7 @@ //! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! -//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed. +//! Attributes should be added to `crate::context:ATTRIBUTE_MAPPING` to be parsed. use std::marker::PhantomData; @@ -74,7 +74,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait SingleAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &[rustc_span::Symbol]; const ATTRIBUTE_ORDER: AttributeOrder; const ON_DUPLICATE: OnDuplicate; @@ -142,7 +142,7 @@ pub(crate) enum OnDuplicate { /// Custom function called when a duplicate attribute is found. /// /// - `unused` is the span of the attribute that was unused or bad because of some - /// duplicate reason (see [`AttributeDuplicates`]) + /// duplicate reason (see [`AttributeOrder`]) /// - `used` is the span of the attribute that was used in favor of the unused attribute Custom(fn(cx: &AcceptContext<'_, '_, S>, used: Span, unused: Span)), } @@ -210,7 +210,7 @@ type ConvertFn = fn(ThinVec) -> AttributeKind; /// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. pub(crate) trait CombineAttributeParser: 'static { - const PATH: &'static [rustc_span::Symbol]; + const PATH: &[rustc_span::Symbol]; type Item; const CONVERT: ConvertFn; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 1ee4359dcdac..45822698101d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -21,7 +21,7 @@ pub(crate) struct ReprParser; impl CombineAttributeParser for ReprParser { type Item = (ReprAttr, Span); - const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; + const PATH: &[rustc_span::Symbol] = &[sym::repr]; const CONVERT: ConvertFn = AttributeKind::Repr; fn extend<'c>( diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 00b9e23d3a73..10528fd3e5fb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -121,7 +121,7 @@ impl AttributeParser for BodyStabilityParser { pub(crate) struct ConstStabilityIndirectParser; // FIXME(jdonszelmann): single word attribute group when we have these impl SingleAttributeParser for ConstStabilityIndirectParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; + const PATH: &[rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index f3d4327d897e..3841462f31ab 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -12,7 +12,7 @@ pub(crate) struct TransparencyParser; #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] impl SingleAttributeParser for TransparencyParser { - const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + const PATH: &[rustc_span::Symbol] = &[sym::rustc_macro_transparency]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 90642e032d08..2c5cdade9718 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -84,7 +84,7 @@ #[macro_use] mod attributes; -mod context; +pub(crate) mod context; pub mod parser; mod session_diagnostics;