diff options
| author | Jana Dönszelmann <jana@donsz.nl> | 2025-02-12 13:59:08 +0100 |
|---|---|---|
| committer | Jana Dönszelmann <jana@donsz.nl> | 2025-06-12 09:56:47 +0200 |
| commit | 6072207a1151f212163e3c19d92ff4ea22f291b2 (patch) | |
| tree | 53855f9f1b959d32f14f0ae33f4cdd8c17f9ea88 | |
| parent | 4e1b6d13a20e5b72922f085fb4b248848ca02910 (diff) | |
| download | rust-6072207a1151f212163e3c19d92ff4ea22f291b2.tar.gz rust-6072207a1151f212163e3c19d92ff4ea22f291b2.zip | |
introduce new lint infra
lint on duplicates during attribute parsing To do this we stuff them in the diagnostic context to be emitted after hir is constructed
32 files changed, 663 insertions, 227 deletions
diff --git a/Cargo.lock b/Cargo.lock index 93abab8469a..5783cd3f941 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3787,6 +3787,7 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_attr_data_structures", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 537d4a2a6af..718edad0cc6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -73,16 +73,15 @@ 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 attrs = &*self.arena.alloc_from_iter( - self.lower_attrs_vec(&e.attrs, e.span) - .into_iter() - .chain(old_attrs.iter().cloned()), - ); - if attrs.is_empty() { + let new_attrs = self + .lower_attrs_vec(&e.attrs, e.span, ex.hir_id) + .into_iter() + .chain(old_attrs.iter().cloned()); + let new_attrs = &*self.arena.alloc_from_iter(new_attrs); + if new_attrs.is_empty() { return ex; } - - self.attrs.insert(ex.hir_id.local_id, attrs); + self.attrs.insert(ex.hir_id.local_id, new_attrs); } return ex; } @@ -2035,7 +2034,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/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b99df8bd7e5..249d1a99d72 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,6 +51,7 @@ 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::lints::DelayedLint; use rustc_hir::{ self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate, @@ -141,6 +142,8 @@ struct LoweringContext<'a, 'hir> { allow_for_await: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>, + delayed_lints: Vec<DelayedLint>, + attribute_parser: AttributeParser<'hir>, } @@ -190,6 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools), + delayed_lints: Vec::new(), } } @@ -198,6 +202,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } +struct SpanLowerer { + is_incremental: bool, + def_id: LocalDefId, +} + +impl SpanLowerer { + fn lower(&self, span: Span) -> Span { + if self.is_incremental { + span.with_parent(Some(self.def_id)) + } 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<Vec<usize>> { @@ -573,6 +593,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1)); let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs); let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds); + let current_delayed_lints = std::mem::take(&mut self.delayed_lints); // Do not reset `next_node_id` and `node_id_to_def_id`: // we want `f` to be able to refer to the `LocalDefId`s that the caller created. @@ -606,6 +627,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.item_local_id_counter = current_local_counter; self.impl_trait_defs = current_impl_trait_defs; self.impl_trait_bounds = current_impl_trait_bounds; + self.delayed_lints = current_delayed_lints; debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id)); self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info))); @@ -616,6 +638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut bodies = std::mem::take(&mut self.bodies); let define_opaque = std::mem::take(&mut self.define_opaque); let trait_map = std::mem::take(&mut self.trait_map); + let delayed_lints = std::mem::take(&mut self.delayed_lints).into_boxed_slice(); #[cfg(debug_assertions)] for (id, attrs) in attrs.iter() { @@ -629,14 +652,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bodies = SortedMap::from_presorted_elements(bodies); // Don't hash unless necessary, because it's expensive. - let (opt_hash_including_bodies, attrs_hash) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque); + let (opt_hash_including_bodies, attrs_hash, delayed_lints_hash) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs, &delayed_lints, define_opaque); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque }; + let delayed_lints = + hir::lints::DelayedLints { lints: delayed_lints, opt_hash: delayed_lints_hash }; - self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) + self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map, delayed_lints }) } /// This method allocates a new `HirId` for the given `NodeId`. @@ -759,15 +784,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } + fn span_lowerer(&self) -> SpanLowerer { + SpanLowerer { + is_incremental: self.tcx.sess.opts.incremental.is_some(), + def_id: 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 { @@ -889,7 +916,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); @@ -909,9 +936,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> { - 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<hir::Attribute> { + let l = self.span_lowerer(); + self.attribute_parser.parse_attribute_list( + attrs, + target_span, + target_hir_id, + OmitDoc::Lower, + |s| l.lower(s), + |l| { + self.delayed_lints.push(DelayedLint::AttributeParsing(l)); + }, + ) } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index dbfc95b047a..b0fc19d1cd7 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -8,6 +8,8 @@ mod attributes; mod stability; mod version; +pub mod lints; + use std::num::NonZero; pub use attributes::*; diff --git a/compiler/rustc_attr_data_structures/src/lints.rs b/compiler/rustc_attr_data_structures/src/lints.rs new file mode 100644 index 00000000000..7e3664b2263 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/lints.rs @@ -0,0 +1,14 @@ +use rustc_macros::HashStable_Generic; +use rustc_span::Span; + +#[derive(Clone, Debug, HashStable_Generic)] +pub struct AttributeLint<Id> { + pub id: Id, + pub span: Span, + pub kind: AttributeLintKind, +} + +#[derive(Clone, Debug, HashStable_Generic)] +pub enum AttributeLintKind { + UnusedDuplicate { this: Span, other: Span, warning: bool }, +} diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 45174c9582d..573854afa74 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 d0465546b73..2349ee15fe6 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<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser { const PATH: &'static [Symbol] = &[sym::allow_internal_unstable]; type Item = (Symbol, Span); const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + '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<Item = Self::Item> { + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) } } pub(crate) struct AllowConstFnUnstableParser; -impl CombineAttributeParser for AllowConstFnUnstableParser { +impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser { const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a { - parse_unstable(cx, args, Self::PATH[0]) + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) } } -fn parse_unstable<'a>( - cx: &AcceptContext<'_>, - args: &'a ArgParser<'a>, +fn parse_unstable<S: Stage>( + cx: &AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, symbol: Symbol, ) -> impl IntoIterator<Item = Symbol> { 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 6cff952fcf2..afd3c012f05 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<Span>, } -impl AttributeParser for ConfusablesParser { - const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| { +impl<S: Stage> AttributeParser<S> for ConfusablesParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[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<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { 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 1b18ada70fc..ba1bdb9fd02 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -2,16 +2,16 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::{Span, Symbol, sym}; use super::util::parse_version; -use super::{AttributeDuplicates, OnDuplicate, 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<S: Stage>( + cx: &AcceptContext<'_, '_, S>, name: Symbol, param_span: Span, arg: &ArgParser<'_>, @@ -41,12 +41,12 @@ fn get( } } -impl SingleAttributeParser for DeprecationParser { +impl<S: Stage> SingleAttributeParser<S> for DeprecationParser { const PATH: &'static [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<S> = OnDuplicate::Error; - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let features = cx.features(); let mut since = None; diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs new file mode 100644 index 00000000000..c7f82082c2e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -0,0 +1,97 @@ +// FIXME(jdonszelmann): merge these two parsers and error when both attributes are present here. +// note: need to model better how duplicate attr errors work when not using +// SingleAttributeParser which is what we have two of here. + +use rustc_attr_data_structures::lints::AttributeLintKind; +use rustc_attr_data_structures::{AttributeKind, InlineAttr}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use super::{AcceptContext, AttributeOrder, OnDuplicate}; +use crate::attributes::SingleAttributeParser; +use crate::context::Stage; +use crate::parser::ArgParser; + +pub(crate) struct InlineParser; + +impl<S: Stage> SingleAttributeParser<S> for InlineParser { + const PATH: &'static [Symbol] = &[sym::inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + match args { + ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)), + ArgParser::List(list) => { + let Some(l) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + match l.meta_item().and_then(|i| i.word_without_args().map(|i| i.name)) { + Some(sym::always) => { + Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span)) + } + Some(sym::never) => { + Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span)) + } + _ => { + cx.expected_specific_argument(l.span(), vec!["always", "never"]); + return None; + } + } + } + ArgParser::NameValue(_) => { + let suggestions = + <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline"); + cx.emit_lint( + AttributeLintKind::IllFormedAttributeInput { suggestions }, + cx.attr_span, + ); + return None; + } + } + } +} + +pub(crate) struct RustcForceInlineParser; + +impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser { + const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason"); + + fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let reason = match args { + ArgParser::NoArgs => None, + ArgParser::List(list) => { + let Some(l) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + let Some(reason) = l.lit().and_then(|i| i.kind.str()) else { + cx.expected_string_literal(l.span()); + return None; + }; + + Some(reason) + } + ArgParser::NameValue(v) => { + let Some(reason) = v.value_as_str() else { + cx.expected_string_literal(v.value_span); + return None; + }; + + Some(reason) + } + }; + + Some(AttributeKind::Inline( + InlineAttr::Force { attr_span: cx.attr_span, reason }, + cx.attr_span, + )) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 852615b4fce..1d239bd43f4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,10 +17,11 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::lints::AttributeLintKind; 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; @@ -33,8 +34,8 @@ pub(crate) mod stability; pub(crate) mod transparency; pub(crate) mod util; -type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>); -type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)]; +type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>); +type AcceptMapping<T, S> = &'static [(&'static [Symbol], AcceptFn<T, S>)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -55,11 +56,11 @@ type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)]; /// /// For a simpler attribute parsing interface, consider using [`SingleAttributeParser`] /// or [`CombineAttributeParser`] instead. -pub(crate) trait AttributeParser: Default + 'static { +pub(crate) trait AttributeParser<S: Stage>: 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<Self>; + const ATTRIBUTES: AcceptMapping<Self, S>; /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. @@ -69,7 +70,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// that'd be equivalent to unconditionally applying an attribute to /// every single syntax item that could have attributes applied to it. /// Your accept mappings should determine whether this returns something. - fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>; + fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>; } /// Alternative to [`AttributeParser`] that automatically handles state management. @@ -81,54 +82,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<S: Stage>: 'static { const PATH: &'static [Symbol]; - - const ON_DUPLICATE_STRATEGY: AttributeDuplicates; - const ON_DUPLICATE: OnDuplicate; + const ATTRIBUTE_ORDER: AttributeOrder; + const ON_DUPLICATE: OnDuplicate<S>; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>; + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>; } -pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>); +pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>( + PhantomData<(S, T)>, + Option<(AttributeKind, Span)>, +); -impl<T: SingleAttributeParser> Default for Single<T> { +impl<T: SingleAttributeParser<S>, S: Stage> Default for Single<T, S> { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl<T: SingleAttributeParser> AttributeParser for Single<T> { - const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, 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::<T>(cx, cx.attr_span, unused); - return; +impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> { + const ATTRIBUTES: AcceptMapping<Self, S> = + &[(T::PATH, |group: &mut Single<T, S>, 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::<T>(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::<T>(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::<T>(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<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { 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<S: Stage> { /// Give a default warning Warn, @@ -146,18 +153,25 @@ 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<P: SingleAttributeParser>(&self, cx: &AcceptContext<'_>, used: Span, unused: Span) { +impl<S: Stage> OnDuplicate<S> { + fn exec<P: SingleAttributeParser<S>>( + &self, + cx: &mut AcceptContext<'_, '_, S>, + used: Span, + unused: Span, + ) { match self { - OnDuplicate::Warn => { - todo!() - } - OnDuplicate::WarnButFutureError => { - todo!() - } + OnDuplicate::Warn => cx.emit_lint( + AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false }, + unused, + ), + OnDuplicate::WarnButFutureError => cx.emit_lint( + AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true }, + unused, + ), OnDuplicate::Error => { cx.emit_err(UnusedMultiple { this: used, @@ -172,14 +186,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. @@ -187,7 +204,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<E> = fn(ThinVec<E>) -> AttributeKind; @@ -199,35 +216,35 @@ type ConvertFn<E> = fn(ThinVec<E>) -> 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<S: Stage>: 'static { const PATH: &'static [Symbol]; type Item; const CONVERT: ConvertFn<Self::Item>; /// 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<Item = Self::Item> + 'a; + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c; } -pub(crate) struct Combine<T: CombineAttributeParser>( - PhantomData<T>, - ThinVec<<T as CombineAttributeParser>::Item>, +pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>( + PhantomData<(S, T)>, + ThinVec<<T as CombineAttributeParser<S>>::Item>, ); -impl<T: CombineAttributeParser> Default for Combine<T> { +impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> { fn default() -> Self { Self(Default::default(), Default::default()) } } -impl<T: CombineAttributeParser> AttributeParser for Combine<T> { - const ATTRIBUTES: AcceptMapping<Self> = - &[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))]; +impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> { + const ATTRIBUTES: AcceptMapping<Self, S> = + &[(T::PATH, |group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)))]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { 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 69316541e19..8b2981ddb08 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::{DUMMY_SP, 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<S: Stage> CombineAttributeParser<S> for ReprParser { type Item = (ReprAttr, Span); const PATH: &'static [Symbol] = &[sym::repr]; const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr; - fn extend<'a>( - cx: &'a AcceptContext<'a>, - args: &'a ArgParser<'a>, - ) -> impl IntoIterator<Item = Self::Item> + 'a { + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { let mut reprs = Vec::new(); let Some(list) = args.list() else { @@ -91,7 +91,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> { } } -fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> { +fn parse_repr<S: Stage>( + cx: &AcceptContext<'_, '_, S>, + param: &MetaItemParser<'_>, +) -> Option<ReprAttr> { use ReprAttr::*; // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the @@ -180,8 +183,8 @@ enum AlignKind { Align, } -fn parse_repr_align( - cx: &AcceptContext<'_>, +fn parse_repr_align<S: Stage>( + 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 e8653453b39..d8cf56a9830 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -8,10 +8,8 @@ use rustc_errors::ErrorGuaranteed; use rustc_span::{Span, Symbol, 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}; @@ -33,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<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -43,8 +41,8 @@ impl StabilityParser { } } -impl AttributeParser for StabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = &[ +impl<S: Stage> AttributeParser<S> for StabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[ (&[sym::stable], |this, cx, args| { reject_outside_std!(cx); if !this.check_duplicate(cx) @@ -67,7 +65,7 @@ impl AttributeParser for StabilityParser { }), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if let Some(atum) = self.allowed_through_unstable_modules { if let Some(( Stability { @@ -97,8 +95,8 @@ pub(crate) struct BodyStabilityParser { stability: Option<(DefaultBodyStability, Span)>, } -impl AttributeParser for BodyStabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = +impl<S: Stage> AttributeParser<S> for BodyStabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[(&[sym::rustc_default_body_unstable], |this, cx, args| { reject_outside_std!(cx); if this.stability.is_some() { @@ -109,7 +107,7 @@ impl AttributeParser for BodyStabilityParser { } })]; - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { let (stability, span) = self.stability?; Some(AttributeKind::BodyStability { stability, span }) @@ -118,12 +116,12 @@ impl AttributeParser for BodyStabilityParser { pub(crate) struct ConstStabilityIndirectParser; // FIXME(jdonszelmann): single word attribute group when we have these -impl SingleAttributeParser for ConstStabilityIndirectParser { +impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser { const PATH: &'static [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<S> = OnDuplicate::Ignore; - fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> { + fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> { Some(AttributeKind::ConstStabilityIndirect) } } @@ -136,7 +134,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<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -146,8 +144,8 @@ impl ConstStabilityParser { } } -impl AttributeParser for ConstStabilityParser { - const ATTRIBUTES: AcceptMapping<Self> = &[ +impl<S: Stage> AttributeParser<S> for ConstStabilityParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[ (&[sym::rustc_const_stable], |this, cx, args| { reject_outside_std!(cx); @@ -177,7 +175,7 @@ impl AttributeParser for ConstStabilityParser { }), ]; - fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> { + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if self.promotable { if let Some((ref mut stab, _)) = self.stability { stab.promotable = true; @@ -197,8 +195,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<S: Stage>( + cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser<'_>, item: &mut Option<Symbol>, ) -> Option<()> { @@ -224,8 +222,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<S: Stage>( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -290,8 +288,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<S: Stage>( + 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 22d2806e41e..1de3a9cfeba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -2,7 +2,8 @@ use rustc_attr_data_structures::AttributeKind; use rustc_span::hygiene::Transparency; use rustc_span::{Symbol, 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<S: Stage> SingleAttributeParser<S> for TransparencyParser { const PATH: &[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<S> = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { match args.name_value().and_then(|nv| nv.value_as_str()) { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 35fb768ad0b..47f72232828 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,13 +1,17 @@ use std::cell::RefCell; use std::collections::BTreeMap; -use std::ops::Deref; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; +use private::Sealed; use rustc_ast as ast; +use rustc_ast::NodeId; use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::Features; -use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId}; +use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId}; use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; @@ -22,20 +26,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], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>, + Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>> + )> + }; +} + macro_rules! attribute_parsers { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - type Accepts = BTreeMap< - &'static [Symbol], - Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)> - >; - type Finalizes = Vec< - Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>> - >; - pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| { - let mut accepts = Accepts::new(); - let mut finalizes = Finalizes::new(); + mod early { + use super::*; + type Combine<T> = super::Combine<T, Early>; + type Single<T> = super::Single<T, Early>; + + attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];); + } + mod late { + use super::*; + type Combine<T> = super::Combine<T, Late>; + type Single<T> = super::Single<T, Late>; + + attribute_parsers!(@[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::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new(); + let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new(); $( { thread_local! { @@ -62,7 +86,6 @@ macro_rules! attribute_parsers { }); }; } - attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start @@ -86,50 +109,114 @@ attribute_parsers!( ]; ); +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; + + fn parsers() -> &'static group_type!(Self); + + fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed; +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Early { + type Id = NodeId; + + fn parsers() -> &'static group_type!(Self) { + &early::ATTRIBUTE_PARSERS + } + fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + sess.dcx().create_err(diag).delay_as_bug() + } +} + +// allow because it's a sealed trait +#[allow(private_interfaces)] +impl Stage for Late { + type Id = HirId; + + fn parsers() -> &'static group_type!(Self) { + &late::ATTRIBUTE_PARSERS + } + fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + tcx.dcx().emit_err(diag) + } +} + +/// 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) finalize_cx: &'a FinalizeContext<'a>, +pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { + pub(crate) finalize_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(&mut self, lint: AttributeLintKind, span: Span) { + let id = self.target_id; + (self.emit_lint)(AttributeLint { id, span, kind: lint }); } } -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.finalize_cx } } +impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.finalize_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, + + pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<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 } } @@ -141,23 +228,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<Symbol>, - sess: &'sess Session, features: Option<&'sess Features>, + sess: &'sess Session, + stage: PhantomData<S>, /// *Only* parse attributes with this symbol. /// /// Used in cases where we want the lowering infrastructure for parse just a single attribute. parse_only: Option<Symbol>, - - /// 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. @@ -168,33 +252,53 @@ 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<Attribute> { - let mut parsed = Self { - sess, + let mut p = Self { features: None, tools: Vec::new(), parse_only: Some(sym), - limit_diagnostics, - } - .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity); - + sess, + stage: PhantomData, + }; + let mut parsed = p.parse_attribute_list( + attrs, + target_span, + target_node_id, + OmitDoc::Skip, + std::convert::identity, + |_lint| { + panic!("can't emit lints here for now (nothing uses this atm)"); + }, + ); assert!(parsed.len() <= 1); parsed.pop() } + pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self { + Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } + } +} + +impl<'sess> AttributeParser<'sess, Late> { pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self { - Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false } + Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } } +} +impl<'sess, S: Stage> AttributeParser<'sess, S> { pub(crate) fn sess(&self) -> &'sess Session { - self.sess + &self.sess } pub(crate) fn features(&self) -> &'sess Features { @@ -202,25 +306,25 @@ 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, + mut emit_lint: impl FnMut(AttributeLint<S::Id>), ) -> Vec<Attribute> { let mut attributes = Vec::new(); - let finalize_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. if let Some(expected) = self.parse_only { @@ -268,13 +372,18 @@ impl<'sess> AttributeParser<'sess> { let args = parser.args(); let parts = path.segments().map(|i| i.name).collect::<Vec<_>>(); - if let Some(accept) = ATTRIBUTE_PARSERS.0.get(parts.as_slice()) { - let cx = AcceptContext { - finalize_cx: &finalize_cx, + if let Some(accept) = S::parsers().0.get(parts.as_slice()) { + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + finalize_cx: FinalizeContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, attr_span: lower_span(attr.span), }; - accept(&cx, &args) + accept(&mut cx, args) } else { // If we're here, we must be compiling a tool attribute... Or someone // forgot to parse their fancy new attribute. Let's warn them in any case. @@ -304,8 +413,13 @@ impl<'sess> AttributeParser<'sess> { } let mut parsed_attributes = Vec::new(); - for f in &ATTRIBUTE_PARSERS.1 { - if let Some(attr) = f(&finalize_cx) { + for f in &S::parsers().1 { + if let Some(attr) = f(&mut FinalizeContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }) { 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 15037e802ff..5c458575c76 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -62,7 +62,7 @@ //! a "stability" of an item. So, the stability attribute has an //! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]` //! and `#[unstable()]` syntactic attributes, and at the end produce a single -//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability). +//! [`AttributeKind::Stability`]. //! //! When multiple instances of the same attribute are allowed, they're combined into a single //! semantic attribute. For example: @@ -86,6 +86,7 @@ #[macro_use] mod attributes; mod context; +mod lints; pub mod parser; mod session_diagnostics; @@ -93,6 +94,7 @@ pub use attributes::cfg::*; pub use attributes::util::{ find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, }; -pub use context::{AttributeParser, OmitDoc}; +pub use context::{AttributeParser, Early, Late, OmitDoc}; +pub use lints::emit_attribute_lint; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs new file mode 100644 index 00000000000..d0d112446b4 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -0,0 +1,19 @@ +use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; +use rustc_errors::LintEmitter; +use rustc_hir::HirId; + +use crate::session_diagnostics; + +pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) { + let AttributeLint { id, span, kind } = lint; + + match kind { + &AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + *id, + *span, + session_diagnostics::UnusedDuplicate { this, other, warning }, + ), + } +} diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index e10e3b511db..1edbe3a9d27 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -115,7 +115,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 => { @@ -235,7 +235,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), @@ -320,13 +320,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<TokenStreamIter<'a>>, - 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() } @@ -507,11 +507,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 2c434175b4b..271ea15740f 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 6dd3adf750d..d642851bb77 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -484,7 +484,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 133bd361ee7..9f72fc4705a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -60,8 +60,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}; @@ -101,6 +102,19 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24); #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); +/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`. +/// Always the `TyCtxt`. +pub trait LintEmitter: Copy { + #[track_caller] + fn emit_node_span_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + decorator: impl for<'a> LintDiagnostic<'a, ()>, + ); +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { /// Hide the suggested code when displaying this suggestion inline. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6f288bb39b9..336bf0d93fd 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -34,6 +34,7 @@ use crate::def::{CtorKind, DefKind, PerNS, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; +use crate::lints::DelayedLints; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] pub enum AngleBrackets { @@ -1526,6 +1527,10 @@ pub struct OwnerInfo<'hir> { /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. pub trait_map: ItemLocalMap<Box<[TraitCandidate]>>, + + /// Lints delayed during ast lowering to be emitted + /// after hir has completely built + pub delayed_lints: DelayedLints, } impl<'tcx> OwnerInfo<'tcx> { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c6fe475b460..dafd31336fb 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -27,6 +27,7 @@ mod hir; pub mod hir_id; pub mod intravisit; pub mod lang_items; +pub mod lints; pub mod pat_util; mod stable_hash_impls; mod target; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs new file mode 100644 index 00000000000..7be6c32e57e --- /dev/null +++ b/compiler/rustc_hir/src/lints.rs @@ -0,0 +1,23 @@ +use rustc_attr_data_structures::lints::AttributeLint; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_macros::HashStable_Generic; + +use crate::HirId; + +/// During ast lowering, no lints can be emitted. +/// That is because lints attach to nodes either in the AST, or on the built HIR. +/// When attached to AST nodes, they're emitted just before building HIR, +/// and then there's a gap where no lints can be emitted until HIR is done. +/// The variants in this enum represent lints that are temporarily stashed during +/// AST lowering to be emitted once HIR is built. +#[derive(Clone, Debug, HashStable_Generic)] +pub enum DelayedLint { + AttributeParsing(AttributeLint<HirId>), +} + +#[derive(Debug)] +pub struct DelayedLints { + pub lints: Box<[DelayedLint]>, + // Only present when the crate hash is needed. + pub opt_hash: Option<Fingerprint>, +} diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 91ea88cae47..6acf1524b60 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -6,6 +6,7 @@ use crate::hir::{ AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; use crate::hir_id::{HirId, ItemLocalId}; +use crate::lints::DelayedLints; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro @@ -102,6 +103,13 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<' } } +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for DelayedLints { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + let DelayedLints { opt_hash, .. } = *self; + opt_hash.unwrap().hash_stable(hcx, hasher); + } +} + impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { // We ignore the `map` since it refers to information included in `opt_hash` which is diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 899370b34e4..5d6c49ee862 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -14,6 +14,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index a92ee89186c..007f3a6abf6 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -92,8 +92,9 @@ mod variance; pub use errors::NoVariantNamed; use rustc_abi::ExternAbi; -use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::lints::DelayedLint; +use rustc_hir::{self as hir}; use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; @@ -174,6 +175,14 @@ pub fn provide(providers: &mut Providers) { }; } +fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { + match lint { + DelayedLint::AttributeParsing(attribute_lint) => { + rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx) + } + } +} + pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); @@ -192,6 +201,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); + for owner_id in tcx.hir_crate_items(()).owners() { + if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { + for lint in &delayed_lints.lints { + emit_delayed_lint(lint, tcx); + } + } + } + tcx.par_hir_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); // Make sure we evaluate all static and (non-associated) const items, even if unused. diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 31c18074466..1b60466a589 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -164,7 +164,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_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index d1f5caaafb2..cb4760c18de 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -12,6 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; +use rustc_hir::lints::DelayedLint; use rustc_hir::*; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; @@ -161,8 +162,9 @@ impl<'tcx> TyCtxt<'tcx> { node: OwnerNode<'_>, bodies: &SortedMap<ItemLocalId, &Body<'_>>, attrs: &SortedMap<ItemLocalId, &[Attribute]>, + delayed_lints: &[DelayedLint], define_opaque: Option<&[(Span, LocalDefId)]>, - ) -> (Option<Fingerprint>, Option<Fingerprint>) { + ) -> (Option<Fingerprint>, Option<Fingerprint>, Option<Fingerprint>) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); @@ -178,10 +180,16 @@ impl<'tcx> TyCtxt<'tcx> { define_opaque.hash_stable(&mut hcx, &mut stable_hasher); let h2 = stable_hasher.finish(); - (Some(h1), Some(h2)) + + // hash lints emitted during ast lowering + let mut stable_hasher = StableHasher::new(); + delayed_lints.hash_stable(&mut hcx, &mut stable_hasher); + let h3 = stable_hasher.finish(); + + (Some(h1), Some(h2), Some(h3)) }) } else { - (None, None) + (None, None, None) } } } @@ -214,6 +222,8 @@ pub fn provide(providers: &mut Providers) { providers.hir_attr_map = |tcx, id| { tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; + providers.opt_ast_lowering_delayed_lints = + |tcx, id| tcx.hir_crate(()).owners[id.def_id].as_owner().map(|o| &o.delayed_lints); providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)); providers.def_ident_span = |tcx, def_id| { let hir_id = tcx.local_def_id_to_hir_id(def_id); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d8c927e00db..15e8c1ef3cc 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -221,6 +221,14 @@ rustc_queries! { feedable } + /// Gives access to lints emitted during ast lowering. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { + desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } + } + /// Returns the *default* of the const pararameter given by `DefId`. /// /// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index cb132ae5573..6a47000fc85 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -30,7 +30,7 @@ use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -1350,8 +1350,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { let bodies = Default::default(); let attrs = hir::AttributeMap::EMPTY; - let (opt_hash_including_bodies, _) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque); + let (opt_hash_including_bodies, _, _) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, &[], attrs.define_opaque); let node = node.into(); self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes { opt_hash_including_bodies, @@ -1389,6 +1389,18 @@ pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } +impl<'tcx> LintEmitter for TyCtxt<'tcx> { + fn emit_node_span_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + decorator: impl for<'a> LintDiagnostic<'a, ()>, + ) { + self.emit_node_span_lint(lint, hir_id, span, decorator); + } +} + // Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its // field are asserted to implement these traits below, so this is trivially safe, and it greatly // speeds-up compilation of this crate and its dependents. diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index dc16fe212b1..f8e0a6936a0 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -128,7 +128,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(), @@ -136,8 +136,14 @@ 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, + |_l| { + // FIXME(jdonszelmann): emit lints here properly + // NOTE that before new attribute parsing, they didn't happen either + // but it would be nice if we could change that. + }, ); let macro_data = |
