diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index c3222b79e55c9..c49f8dd1d0873 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,4 +1,5 @@ use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; +use rustc_attr_parsing::AttrTarget; use rustc_hir as hir; use rustc_span::sym; use smallvec::SmallVec; @@ -109,7 +110,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; - self.lower_attrs(hir_id, &l.attrs, l.span); + self.lower_attrs(hir_id, &l.attrs, AttrTarget::Todo, l.span); self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source }) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 718edad0cc61e..84e5892809d27 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_ast_pretty::pprust::expr_to_string; +use rustc_attr_parsing::AttrTarget; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::HirId; @@ -74,7 +75,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); let new_attrs = self - .lower_attrs_vec(&e.attrs, e.span, ex.hir_id) + .lower_attrs_vec(&e.attrs, AttrTarget::Todo, e.span, ex.hir_id) .into_iter() .chain(old_attrs.iter().cloned()); let new_attrs = &*self.arena.alloc_from_iter(new_attrs); @@ -97,7 +98,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let expr_hir_id = self.lower_node_id(e.id); - self.lower_attrs(expr_hir_id, &e.attrs, e.span); + self.lower_attrs(expr_hir_id, &e.attrs, AttrTarget::Todo, e.span); let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), @@ -677,7 +678,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond)); let hir_id = self.next_id(); let span = self.lower_span(arm.span); - self.lower_attrs(hir_id, &arm.attrs, arm.span); + self.lower_attrs(hir_id, &arm.attrs, AttrTarget::Todo, arm.span); let is_never_pattern = pat.is_never_pattern(); // We need to lower the body even if it's unneeded for never pattern in match, // ensure that we can get HirId for DefId if need (issue #137708). @@ -849,6 +850,7 @@ impl<'hir> LoweringContext<'_, 'hir> { style: AttrStyle::Outer, span: unstable_span, }], + AttrTarget::Todo, span, ); } @@ -1690,7 +1692,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs, f.span); + self.lower_attrs(hir_id, &f.attrs, AttrTarget::Todo, f.span); hir::ExprField { hir_id, ident: self.lower_ident(f.ident), @@ -1946,7 +1948,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // // Also, add the attributes to the outer returned expr node. let expr = self.expr_drop_temps_mut(for_span, match_expr); - self.lower_attrs(expr.hir_id, &e.attrs, e.span); + self.lower_attrs(expr.hir_id, &e.attrs, AttrTarget::Todo, e.span); expr } @@ -2003,7 +2005,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let val_ident = Ident::with_dummy_span(sym::val); let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); let val_expr = self.expr_ident(span, val_ident, val_pat_nid); - self.lower_attrs(val_expr.hir_id, &attrs, span); + self.lower_attrs(val_expr.hir_id, &attrs, AttrTarget::Todo, span); let continue_pat = self.pat_cf_continue(unstable_span, val_pat); self.arm(continue_pat, val_expr) }; @@ -2034,7 +2036,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, span); + self.lower_attrs(ret_expr.hir_id, &attrs, AttrTarget::Todo, span); let break_pat = self.pat_cf_break(try_span, residual_local); self.arm(break_pat, ret_expr) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 49110c93954ad..708e31875083a 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2,6 +2,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; +use rustc_attr_parsing::AttrTarget; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; @@ -82,7 +83,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { self.with_lctx(CRATE_NODE_ID, |lctx| { let module = lctx.lower_mod(&c.items, &c.spans); // FIXME(jdonszelman): is dummy span ever a problem here? - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, AttrTarget::Todo, DUMMY_SP); hir::OwnerNode::Crate(module) }) } @@ -138,7 +139,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { let vis_span = self.lower_span(i.vis.span); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); + let attrs = self.lower_attrs(hir_id, &i.attrs, AttrTarget::from(&i.kind), i.span); let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind); let item = hir::Item { owner_id: hir_id.expect_owner(), @@ -641,7 +642,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let owner_id = hir_id.expect_owner(); - let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); + let attrs = self.lower_attrs(hir_id, &i.attrs, AttrTarget::Todo, i.span); let (ident, kind) = match &i.kind { ForeignItemKind::Fn(box Fn { sig, ident, generics, define_opaque, .. }) => { let fdec = &sig.decl; @@ -716,7 +717,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { let hir_id = self.lower_node_id(v.id); - self.lower_attrs(hir_id, &v.attrs, v.span); + self.lower_attrs(hir_id, &v.attrs, AttrTarget::Todo, v.span); hir::Variant { hir_id, def_id: self.local_def_id(v.id), @@ -778,7 +779,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FieldDef<'hir> { let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs, f.span); + self.lower_attrs(hir_id, &f.attrs, AttrTarget::Todo, f.span); hir::FieldDef { span: self.lower_span(f.span), hir_id, @@ -797,7 +798,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); + let attrs = self.lower_attrs(hir_id, &i.attrs, AttrTarget::from((true, &i.kind)), i.span); let trait_item_def_id = hir_id.expect_owner(); let (ident, generics, kind, has_default) = match &i.kind { @@ -991,7 +992,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); + let attrs = self.lower_attrs( + hir_id, + &i.attrs, + AttrTarget::from((is_in_trait_impl, &i.kind)), + i.span, + ); let (ident, (generics, kind)) = match &i.kind { AssocItemKind::Const(box ConstItem { @@ -1185,7 +1191,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> { let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs, param.span); + self.lower_attrs(hir_id, ¶m.attrs, AttrTarget::Todo, param.span); hir::Param { hir_id, pat: self.lower_pat(¶m.pat), @@ -1894,7 +1900,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> { let hir_id = self.lower_node_id(pred.id); let span = self.lower_span(pred.span); - self.lower_attrs(hir_id, &pred.attrs, span); + self.lower_attrs(hir_id, &pred.attrs, AttrTarget::Todo, span); let kind = self.arena.alloc(match &pred.kind { WherePredicateKind::BoundPredicate(WhereBoundPredicate { bound_generic_params, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 3b99a653417aa..00ba5b983dc59 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -42,7 +42,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; -use rustc_attr_parsing::{AttributeParser, OmitDoc}; +use rustc_attr_parsing::{AttrTarget, AttributeParser, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -911,12 +911,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, id: HirId, attrs: &[Attribute], + target: AttrTarget<'_>, target_span: Span, ) -> &'hir [hir::Attribute] { if attrs.is_empty() { &[] } else { - let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id); + let lowered_attrs = + self.lower_attrs_vec(attrs, target, self.lower_span(target_span), id); assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(lowered_attrs); @@ -939,12 +941,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_attrs_vec( &mut self, attrs: &[Attribute], + target: AttrTarget<'_>, target_span: Span, target_hir_id: HirId, ) -> Vec { let l = self.span_lowerer(); self.attribute_parser.parse_attribute_list( attrs, + target, target_span, target_hir_id, OmitDoc::Lower, @@ -1899,7 +1903,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (name, kind) = self.lower_generic_param_kind(param, source); let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs, param.span()); + self.lower_attrs(hir_id, ¶m.attrs, AttrTarget::Todo, param.span()); hir::GenericParam { hir_id, def_id: self.local_def_id(param.id), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 58dea472f1d3b..a090c26936c23 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use rustc_ast::ptr::P; use rustc_ast::*; +use rustc_attr_parsing::AttrTarget; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, LangItem}; @@ -94,7 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let fs = self.arena.alloc_from_iter(fields.iter().map(|f| { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs, f.span); + self.lower_attrs(hir_id, &f.attrs, AttrTarget::Todo, f.span); hir::PatField { hir_id, diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 1faee41c2a9d6..0bdc2b15a9f84 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -45,6 +45,7 @@ impl SingleAttributeParser for DeprecationParser { const PATH: &[Symbol] = &[sym::deprecated]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const POSITIONS: &[crate::TargetChecker] = &[crate::allowed!(_)]; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let features = cx.features(); diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 32a20d4c5b5e4..228d1e329afdc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -13,6 +13,7 @@ impl SingleAttributeParser for AsPtrParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const POSITIONS: &[crate::TargetChecker] = &[crate::allowed!(Function | Method)]; fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { // FIXME: check that there's no args (this is currently checked elsewhere) diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index df488c89a34fa..a1b647657b435 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -21,6 +21,7 @@ use rustc_attr_data_structures::lints::AttributeLintKind; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; +use crate::TargetChecker; use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics::UnusedMultiple; @@ -87,6 +88,7 @@ pub(crate) trait SingleAttributeParser: 'static { const PATH: &[Symbol]; const ATTRIBUTE_ORDER: AttributeOrder; const ON_DUPLICATE: OnDuplicate; + const POSITIONS: &[TargetChecker]; /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option; @@ -106,6 +108,25 @@ impl, S: Stage> Default for Single { impl, S: Stage> AttributeParser for Single { const ATTRIBUTES: AcceptMapping = &[(T::PATH, |group: &mut Single, cx, args| { + let mut allowed = false; + for checker in T::POSITIONS { + for pos in checker.check { + allowed |= pos(cx.target); + } + } + + if !allowed { + let allowed_targets = crate::AttrTarget::allowed(T::POSITIONS); + cx.dcx().span_err( + cx.attr_span, + format!( + "`{}` is not valid at that position. It is only allowed in {:?}", + crate::attributes::util::PathPrinter(T::PATH), + allowed_targets, + ), + ); + return; + } if let Some(pa) = T::convert(cx, args) { match T::ATTRIBUTE_ORDER { // keep the first and report immediately. ignore this attribute diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6589a51db2b41..b470be343bbf0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -9,6 +9,7 @@ use rustc_span::{Span, Symbol, sym}; use super::util::parse_version; use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use crate::allowed; use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -120,6 +121,7 @@ impl SingleAttributeParser for ConstStabilityIndirectParser { const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; + const POSITIONS: &[crate::TargetChecker] = &[allowed!(_)]; fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { Some(AttributeKind::ConstStabilityIndirect) diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 16ad9d03e500d..a237bd06d7201 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -3,9 +3,9 @@ use rustc_span::hygiene::Transparency; use rustc_span::{Symbol, sym}; use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::allowed; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; - pub(crate) struct TransparencyParser; // FIXME(jdonszelmann): make these proper diagnostics @@ -17,6 +17,7 @@ impl SingleAttributeParser for TransparencyParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); + const POSITIONS: &[crate::TargetChecker] = &[allowed!(_)]; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { match args.name_value().and_then(|nv| nv.value_as_str()) { diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 503d2f1fae168..aed0b007a7c7f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,3 +1,5 @@ +use std::fmt; + use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; use rustc_attr_data_structures::RustcVersion; use rustc_feature::is_builtin_attr_name; @@ -56,3 +58,25 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( } false } + +pub(crate) struct PathPrinter<'s>(pub &'s [Symbol]); + +impl fmt::Display for PathPrinter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "#[")?; + match &self.0 { + &[] => {} + &[one] => { + write!(f, "{one}")?; + } + &[start @ .., last] => { + for segment in start { + write!(f, "{segment}::")?; + } + write!(f, "{last}")?; + } + } + write!(f, "]")?; + Ok(()) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3193d8975e9fe..c268551412df6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,6 +15,7 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; +use crate::AttrTarget; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -200,6 +201,8 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. pub(crate) cx: &'p mut AttributeParser<'sess, S>, + #[allow(dead_code)] + pub(crate) target: AttrTarget<'p>, /// 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 @@ -262,6 +265,7 @@ impl<'sess> AttributeParser<'sess, Early> { sess: &'sess Session, attrs: &[ast::Attribute], sym: Symbol, + target: AttrTarget<'_>, target_span: Span, target_node_id: NodeId, ) -> Option { @@ -274,6 +278,7 @@ impl<'sess> AttributeParser<'sess, Early> { }; let mut parsed = p.parse_attribute_list( attrs, + target, target_span, target_node_id, OmitDoc::Skip, @@ -318,10 +323,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { pub fn parse_attribute_list( &mut self, attrs: &[ast::Attribute], + target: AttrTarget<'_>, target_span: Span, target_id: S::Id, omit_doc: OmitDoc, - lower_span: impl Copy + Fn(Span) -> Span, mut emit_lint: impl FnMut(AttributeLint), ) -> Vec { @@ -378,6 +383,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { finalize_cx: FinalizeContext { cx: self, + target, target_span, target_id, emit_lint: &mut emit_lint, @@ -418,6 +424,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { for f in &S::parsers().1 { if let Some(attr) = f(&mut FinalizeContext { cx: self, + target, target_span, target_id, emit_lint: &mut emit_lint, diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 47eeb63bad3db..3b093e11af6b1 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -89,6 +89,8 @@ pub(crate) mod context; mod lints; pub mod parser; mod session_diagnostics; +#[macro_use] +mod target; pub use attributes::cfg::*; pub use attributes::util::{ @@ -96,5 +98,6 @@ pub use attributes::util::{ }; pub use context::{AttributeParser, Early, Late, OmitDoc}; pub use lints::emit_attribute_lint; +pub use target::*; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/target.rs b/compiler/rustc_attr_parsing/src/target.rs new file mode 100644 index 0000000000000..10022c140d980 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/target.rs @@ -0,0 +1,143 @@ +use rustc_ast as ast; +use rustc_span::{DUMMY_SP, Ident}; +use thin_vec::ThinVec; + +/// Enumeration of things that an attribute can be on. +#[derive(Copy, Clone, Debug)] +pub enum AttrTarget<'ast> { + Function { + function: &'ast ast::Fn, + }, + // Probably should have two variants for trait methods and regular impl methods + Method { + function: &'ast ast::Fn, + }, + + Trait { + // This would be nice for `#[diagnostic::_]` parsing, + // where we'd like to look at the generics. + trait_: &'ast ast::Trait, + }, + + // everything else + Todo, +} + +impl AttrTarget<'_> { + pub fn allowed(checks: &[TargetChecker]) -> Vec<&'static str> { + // This is kinda ugly but we need a default value of the ast type to see if the check would + // succeed. The contents don't need to make sense since the `allowed!` macro forces that it + // cannot be matched on by fields. + // + // Things to make this nicer would be: + // - Implement Default for everything, + // - use the `strum` crate to generate a fieldless version of AttrTarget + // - Maybe some way to get discriminants of enums without needing to instantiate it + // - Some sort of unsafe hax maybe + let generics = ast::Generics::default(); + let fn_sig = ast::FnSig { + header: ast::FnHeader::default(), + decl: Box::new(ast::FnDecl { + inputs: ThinVec::new(), + output: ast::FnRetTy::Default(DUMMY_SP), + }), + span: DUMMY_SP, + }; + let function = &ast::Fn { + defaultness: ast::Defaultness::Final, + ident: Ident::dummy(), + sig: fn_sig, + generics: generics.clone(), + contract: None, + body: None, + define_opaque: None, + }; + let trait_ = &ast::Trait { + safety: ast::Safety::Default, + is_auto: ast::IsAuto::No, + ident: Ident::dummy(), + generics, + bounds: Vec::new(), + items: ThinVec::new(), + }; + + // BTreeSet to handle the case with overlapping + let mut ret = std::collections::BTreeSet::new(); + for c in checks { + for check in c.check { + if check(AttrTarget::Function { function }) { + ret.insert("function"); + } + if check(AttrTarget::Method { function }) { + ret.insert("method"); + } + if check(AttrTarget::Trait { trait_ }) { + ret.insert("trait"); + } + } + } + + ret.into_iter().collect() + } +} + +impl<'ast> From<&'ast ast::ItemKind> for AttrTarget<'ast> { + fn from(kind: &'ast ast::ItemKind) -> Self { + use ast::ItemKind::*; + + match kind { + Fn(f) => AttrTarget::Function { function: &**f }, + Trait(t) => AttrTarget::Trait { trait_: &**t }, + _ => AttrTarget::Todo, + } + } +} + +// FIXME(bool_hater) maybe _is_in_trait_impl should really be an enum, not a bool +impl<'ast> From<(bool, &'ast ast::AssocItemKind)> for AttrTarget<'ast> { + fn from((_is_in_trait_impl, assoc): (bool, &'ast ast::AssocItemKind)) -> Self { + use ast::AssocItemKind::*; + + match assoc { + Fn(f) => AttrTarget::Method { function: &**f }, + _ => AttrTarget::Todo, + } + } +} + +#[macro_export] +macro_rules! allowed { + (_) => {{ + $crate::TargetChecker { + check: &[ + { + fn check(_: $crate::AttrTarget<'_>) -> bool { + true + } + check + } + ], + } + }}; + ($($variant:ident)|*) => {{ + $crate::TargetChecker { + check: &[ + $({ + fn check(target: $crate::AttrTarget<'_>) -> bool { + matches!(target, | $crate::AttrTarget::$variant { .. } ) + } + check + }),* + ], + } + }}; +} + +pub struct TargetChecker { + pub check: &'static [fn(crate::AttrTarget<'_>) -> bool], +} + +pub struct PosError { + pub accepted: Vec<&'static str>, + pub found_on: &'static str, +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index d642851bb77bf..aa20b846efced 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -186,7 +186,7 @@ use rustc_ast::{ Generics, Mutability, PatKind, VariantData, }; use rustc_attr_data_structures::{AttributeKind, ReprPacked}; -use rustc_attr_parsing::AttributeParser; +use rustc_attr_parsing::{AttrTarget, AttributeParser}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_hir::Attribute; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -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, item.id), + AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, AttrTarget::Todo, item.span, item.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 1b60466a589d2..aa4cf25861a4a 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,6 +1,6 @@ use rustc_abi::ExternAbi; use rustc_attr_data_structures::{AttributeKind, ReprAttr}; -use rustc_attr_parsing::AttributeParser; +use rustc_attr_parsing::{AttrTarget, AttributeParser}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind}; @@ -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, it.id), + AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, AttrTarget::Todo, it.span, it.id), Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index f8e0a6936a007..4bf7c80ec26bb 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -3,7 +3,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{AttributeParser, OmitDoc}; +use rustc_attr_parsing::{AttrTarget, AttributeParser, OmitDoc}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -135,6 +135,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ); let attrs = parser.parse_attribute_list( &i.attrs, + AttrTarget::Todo, i.span, i.id, OmitDoc::Skip, diff --git a/tests/ui/woops.rs b/tests/ui/woops.rs new file mode 100644 index 0000000000000..239ea152ae670 --- /dev/null +++ b/tests/ui/woops.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +#[rustc_as_ptr] //~ERROR +pub struct X; diff --git a/tests/ui/woops.stderr b/tests/ui/woops.stderr new file mode 100644 index 0000000000000..47c509f503ff4 --- /dev/null +++ b/tests/ui/woops.stderr @@ -0,0 +1,8 @@ +error: `#[rustc_as_ptr]` is not valid at that position. It is only allowed in ["function", "method"] + --> $DIR/woops.rs:4:1 + | +LL | #[rustc_as_ptr] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error +