diff --git a/Cargo.lock b/Cargo.lock index ad054e50e6029..8a8c911b89077 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3354,6 +3354,7 @@ dependencies = [ "rustc_hir", "rustc_lexer", "rustc_macros", + "rustc_middle", "rustc_session", "rustc_span", "thin-vec", @@ -3788,6 +3789,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 537d4a2a6af6a..718edad0cc61e 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 b99df8bd7e552..20bf540be5f62 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, + attribute_parser: AttributeParser<'hir>, } @@ -189,7 +192,8 @@ 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), + delayed_lints: Vec::new(), } } @@ -198,6 +202,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> { @@ -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(), + 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 { @@ -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 { - 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), + |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 dbfc95b047ae4..b0fc19d1cd7ba 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 0000000000000..7e3664b2263b4 --- /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 { + 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/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 32029137268ba..55553e74cd1e0 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 45174c9582d33..c9443feb021bb 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -131,7 +131,15 @@ 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 = {-passes_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 d0465546b7314..81192f902a232 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 { - const PATH: &'static [Symbol] = &[sym::allow_internal_unstable]; +impl CombineAttributeParser for AllowInternalUnstableParser { + const PATH: &[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 { - const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable]; +impl CombineAttributeParser for AllowConstFnUnstableParser { + const PATH: &[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 6cff952fcf229..afd3c012f05ad 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 006c1fe3b9c98..1faee41c2a9d6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,17 +1,17 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::{Span, Symbol, sym}; -use super::SingleAttributeParser; use super::util::parse_version; -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>, name: Symbol, param_span: Span, arg: &ArgParser<'_>, @@ -41,19 +41,12 @@ fn get( } } -impl SingleAttributeParser for DeprecationParser { - const PATH: &'static [Symbol] = &[sym::deprecated]; +impl SingleAttributeParser for DeprecationParser { + const PATH: &[Symbol] = &[sym::deprecated]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) { - // FIXME(jdonszelmann): merge with errors from check_attrs.rs - cx.emit_err(session_diagnostics::UnusedMultiple { - this: cx.attr_span, - other: first_span, - name: sym::deprecated, - }); - } - - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { 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 0000000000000..c7f82082c2eec --- /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 SingleAttributeParser for InlineParser { + const PATH: &'static [Symbol] = &[sym::inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + 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 = + >::TEMPLATE.suggestions(false, "inline"); + cx.emit_lint( + AttributeLintKind::IllFormedAttributeInput { suggestions }, + cx.attr_span, + ); + return None; + } + } + } +} + +pub(crate) struct RustcForceInlineParser; + +impl SingleAttributeParser for RustcForceInlineParser { + const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason"); + + fn convert(cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + 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 bf18e10e19fd0..d75cc50140eb4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,16 +12,18 @@ //! - [`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; 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; pub(crate) mod allow_unstable; pub(crate) mod cfg; @@ -32,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 [Symbol], AcceptFn)]; +type AcceptFn = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>); +type AcceptMapping = &'static [(&'static [Symbol], AcceptFn)]; /// An [`AttributeParser`] is a type which searches for syntactic attributes. /// @@ -51,15 +53,15 @@ type AcceptMapping = &'static [(&'static [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. @@ -71,43 +73,130 @@ 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 [Symbol]; - - /// Called when a duplicate attribute is found. - /// - /// `first_span` is the span of the first occurrence of this attribute. - // FIXME(jdonszelmann): default error - fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span); +pub(crate) trait SingleAttributeParser: 'static { + const PATH: &[Symbol]; + 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: &mut 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((_, s)) = group.1 { - T::on_duplicate(cx, s); - 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 + AttributeOrder::KeepLast => { + if let Some((_, used)) = group.1 { + T::ON_DUPLICATE.exec::(cx, used, cx.attr_span); + } + } + } + + group.1 = Some((pa, cx.attr_span)); + } + })]; + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + Some(self.1?.0) + } +} - if let Some(pa) = T::convert(cx, args) { - group.1 = Some((pa, cx.attr_span)); - } - })]; +// 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, - fn finalize(self, _cx: &FinalizeContext<'_>) -> Option { - Some(self.1?.0) + /// 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 [`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)), +} + +impl OnDuplicate { + fn exec>( + &self, + cx: &mut AcceptContext<'_, '_, S>, + used: Span, + unused: Span, + ) { + match self { + 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, + 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), + } } } +// +// 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`? + 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. + /// + /// This is the same as `FutureWarnFollowing`, except the last attribute is + /// the one that is "used". Ideally these can eventually migrate to + /// `ErrorPreceding`. + KeepLast, +} type ConvertFn = fn(ThinVec) -> AttributeKind; @@ -118,35 +207,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 { - const PATH: &'static [Symbol]; +pub(crate) trait CombineAttributeParser: 'static { + const PATH: &[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 69316541e1919..753b2366b410e 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 CombineAttributeParser for ReprParser { type Item = (ReprAttr, Span); - const PATH: &'static [Symbol] = &[sym::repr]; + const PATH: &[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 @@ -180,8 +183,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 ce69a54513d2c..3cbadd4c7ec22 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, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeParser, 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) @@ -65,7 +65,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 { @@ -95,8 +95,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() { @@ -107,7 +107,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 }) @@ -116,13 +116,12 @@ 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 [Symbol] = &[sym::rustc_const_stable_indirect]; +impl SingleAttributeParser for ConstStabilityIndirectParser { + const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - // ignore - fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {} - - fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option { + fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { Some(AttributeKind::ConstStabilityIndirect) } } @@ -135,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(&self, cx: &AcceptContext<'_, '_, S>) -> bool { if let Some((_, _)) = self.stability { cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); true @@ -145,8 +144,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); @@ -176,7 +175,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; @@ -196,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( + cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser<'_>, item: &mut Option, ) -> Option<()> { @@ -223,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( + cx: &AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option<(Symbol, StabilityLevel)> { let mut feature = None; @@ -289,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( + 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 d229fc0974010..16ad9d03e500d 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, Symbol, sym}; +use rustc_span::{Symbol, sym}; -use super::{AcceptContext, SingleAttributeParser}; +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, 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 { - const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency]; +impl SingleAttributeParser for TransparencyParser { + const PATH: &[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"); + }); - fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) { - cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); - } - - fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { 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 c02760d830c2a..095e9899446b0 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::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_middle::ty::TyCtxt; 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 Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>, + Vec) -> Option>> + )> + }; +} + macro_rules! attribute_groups { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { - type Accepts = BTreeMap< - &'static [Symbol], - Box, &ArgParser<'_>)> - >; - type Finalizes = Vec< - Box) -> Option> - >; - 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 = 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::<_, Box Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new(); + let mut finalizes = Vec::) -> Option>>::new(); $( { thread_local! { @@ -62,7 +86,6 @@ macro_rules! attribute_groups { }); }; } - attribute_groups!( pub(crate) static ATTRIBUTE_MAPPING = [ // tidy-alphabetical-start @@ -86,50 +109,133 @@ 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; +} + +// 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() + } +} + +// 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) + } +} + +/// 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(&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.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, + + pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint), } -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 +247,19 @@ 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. @@ -168,33 +270,47 @@ 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, + |_lint| { + panic!("can't emit lints here for now (nothing uses this atm)"); + }, + ); 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 { @@ -202,25 +318,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), ) -> 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. if let Some(expected) = self.parse_only { @@ -268,11 +384,18 @@ impl<'sess> AttributeParser<'sess> { let args = parser.args(); let parts = path.segments().map(|i| i.name).collect::>(); - if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) { - let cx = - AcceptContext { group_cx: &group_cx, attr_span: lower_span(attr.span) }; - - accept(&cx, &args) + if let Some(accept) = S::parsers().0.get(parts.as_slice()) { + let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { + group_cx: FinalizeContext { + cx: self, + target_span, + target_id, + emit_lint: &mut emit_lint, + }, + attr_span: lower_span(attr.span), + }; + + 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. @@ -302,8 +425,13 @@ 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, + 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 15037e802ff59..47eeb63bad3db 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -85,7 +85,8 @@ #[macro_use] mod attributes; -mod context; +pub(crate) 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 0000000000000..3647ecdad1839 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -0,0 +1,18 @@ +use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; +use rustc_hir::HirId; +use rustc_middle::ty::TyCtxt; + +use crate::session_diagnostics; + +pub fn emit_attribute_lint(lint: &AttributeLint, tcx: TyCtxt<'_>) { + let AttributeLint { id, span, kind } = lint; + + match kind { + &AttributeLintKind::UnusedDuplicate { this, other, warning } => tcx.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 e10e3b511db60..1edbe3a9d27aa 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>, - 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 2c434175b4b69..7f847d3dd4c5a 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_duplicate)] +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 6dd3adf750dbd..d642851bb77bf 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_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6f288bb39b953..336bf0d93fd03 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>, + + /// 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 c6fe475b4609e..dafd31336fbbc 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 0000000000000..7be6c32e57edc --- /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), +} + +#[derive(Debug)] +pub struct DelayedLints { + pub lints: Box<[DelayedLint]>, + // Only present when the crate hash is needed. + pub opt_hash: Option, +} diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 91ea88cae4776..6acf1524b608f 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 for OwnerNodes<' } } +impl HashStable 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 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 899370b34e498..5d6c49ee862fc 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 a92ee89186cf0..007f3a6abf6c4 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 31c180744668a..1b60466a589d2 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 d1f5caaafb2b3..cb4760c18de5f 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>, attrs: &SortedMap, + delayed_lints: &[DelayedLint], define_opaque: Option<&[(Span, LocalDefId)]>, - ) -> (Option, Option) { + ) -> (Option, Option, Option) { 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 d03f01bf863ed..1b7531db1aebf 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;` this returns `3` for `N`. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0b1e9852d2a90..26612fe4209e5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1335,8 +1335,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, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index dc16fe212b110..f8e0a6936a007 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 =