use std::cell::RefCell; use std::collections::BTreeMap; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use itertools::Itertools; use private::Sealed; use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId}; use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{ AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId, MethodKind, Target, }; use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::link_attrs::{ ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser, }; use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::macro_attrs::{ AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, RustcObjectLifetimeDefaultParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, }; use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::context::MaybeWarn::{Allow, Error, Warn}; use crate::parser::{ArgParser, MetaItemParser, PathParser}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, InvalidTarget, UnknownMetaItem, }; type GroupType = LazyLock>; struct GroupTypeInner { accepters: BTreeMap<&'static [Symbol], Vec>>, finalizers: Vec>, } struct GroupTypeInnerAccept { template: AttributeTemplate, accept_fn: AcceptFn, allowed_targets: AllowedTargets, } type AcceptFn = Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>; type FinalizeFn = Box) -> Option>; macro_rules! attribute_parsers { ( pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { mod early { use super::*; type Combine = super::Combine; type Single = super::Single; type WithoutArgs = super::WithoutArgs; attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];); } mod late { use super::*; type Combine = super::Combine; type Single = super::Single; type WithoutArgs = super::WithoutArgs; attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];); } }; ( @[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?]; ) => { pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| { let mut accepts = BTreeMap::<_, Vec>>::new(); let mut finalizes = Vec::>::new(); $( { thread_local! { static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default()); }; for (path, template, accept_fn) in <$names>::ATTRIBUTES { accepts.entry(*path).or_default().push(GroupTypeInnerAccept { template: *template, accept_fn: Box::new(|cx, args| { STATE_OBJECT.with_borrow_mut(|s| { accept_fn(s, cx, args) }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, }); } finalizes.push(Box::new(|cx| { let state = STATE_OBJECT.take(); state.finalize(cx) })); } )* GroupTypeInner { accepters:accepts, finalizers:finalizes } }); }; } attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start AlignParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, MacroUseParser, NakedParser, StabilityParser, UsedParser, // tidy-alphabetical-end // tidy-alphabetical-start Combine, Combine, Combine, Combine, Combine, // tidy-alphabetical-end // tidy-alphabetical-start Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, Single>, // tidy-alphabetical-end ]; ); mod private { pub trait Sealed {} impl Sealed for super::Early {} impl Sealed for super::Late {} } // allow because it's a sealed trait #[allow(private_interfaces)] pub trait Stage: Sized + 'static + Sealed { type Id: Copy; fn parsers() -> &'static GroupType; fn emit_err<'sess>( &self, sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed; fn should_emit(&self) -> ShouldEmit; } // allow because it's a sealed trait #[allow(private_interfaces)] impl Stage for Early { type Id = NodeId; fn parsers() -> &'static GroupType { &early::ATTRIBUTE_PARSERS } fn emit_err<'sess>( &self, sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { if self.emit_errors.should_emit() { sess.dcx().emit_err(diag) } else { sess.dcx().create_err(diag).delay_as_bug() } } fn should_emit(&self) -> ShouldEmit { self.emit_errors } } // allow because it's a sealed trait #[allow(private_interfaces)] impl Stage for Late { type Id = HirId; fn parsers() -> &'static GroupType { &late::ATTRIBUTE_PARSERS } fn emit_err<'sess>( &self, tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { tcx.dcx().emit_err(diag) } fn should_emit(&self) -> ShouldEmit { ShouldEmit::ErrorsAndLints } } /// used when parsing attributes for miscellaneous things *before* ast lowering pub struct Early { /// Whether to emit errors or delay them as a bug /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted pub emit_errors: ShouldEmit, } /// 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 struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) shared: SharedContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, /// The expected structure of the attribute. /// /// Used in reporting errors to give a hint to users what the attribute *should* look like. pub(crate) template: &'f AttributeTemplate, /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, } impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { self.stage.emit_err(&self.sess, diag) } /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { if !self.stage.should_emit().should_emit() { return; } let id = self.target_id; (self.emit_lint)(AttributeLint { id, span, kind: lint }); } pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { self.emit_lint( AttributeLintKind::UnusedDuplicate { this: unused_span, other: used_span, warning: false, }, unused_span, ) } pub(crate) fn warn_unused_duplicate_future_error( &mut self, used_span: Span, unused_span: Span, ) { self.emit_lint( AttributeLintKind::UnusedDuplicate { this: unused_span, other: used_span, warning: true, }, unused_span, ) } } impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { pub(crate) fn unknown_key( &self, span: Span, found: String, options: &'static [&'static str], ) -> ErrorGuaranteed { self.emit_err(UnknownMetaItem { span, item: found, expected: options }) } /// error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) /// which can make better errors. For example, if the literal was a byte string it will suggest /// removing the `b` prefix. pub(crate) fn expected_string_literal( &self, span: Span, actual_literal: Option<&MetaItemLit>, ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedStringLiteral { byte_string: actual_literal.and_then(|i| { i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) }), }, }) } pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIntegerLiteral, }) } pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedList, }) } pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span: args_span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNoArgs, }) } /// emit an error that a `name` was expected here pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedIdentifier, }) } /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedNameValue(name), }) } /// emit an error that a `name = value` pair was found where that name was already seen. pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::DuplicateKey(key), }) } /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) /// was expected *not* to be a literal, but instead a meta item. pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::UnexpectedLiteral, }) } pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSingleArgument, }) } pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, }) } pub(crate) fn expected_specific_argument( &self, span: Span, possibilities: Vec<&'static str>, ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: false, }, }) } pub(crate) fn expected_specific_argument_and_list( &self, span: Span, possibilities: Vec<&'static str>, ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: true, }, }) } pub(crate) fn expected_specific_argument_strings( &self, span: Span, possibilities: Vec<&'static str>, ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, list: false, }, }) } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span); } } impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> { type Target = SharedContext<'f, 'sess, S>; fn deref(&self) -> &Self::Target { &self.shared } } impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.shared } } /// Context given to every attribute parser during finalization. /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create /// errors, for example. pub struct SharedContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. 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, emit_lint: &'p mut dyn FnMut(AttributeLint), } /// 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<'p, 'sess, S: Stage> { pub(crate) shared: SharedContext<'p, 'sess, S>, /// A list of all attribute on this syntax node. /// /// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize) /// /// Usually, you should use normal attribute parsing logic instead, /// especially when making a *denylist* of other attributes. pub(crate) all_attrs: &'p [PathParser<'p>], } impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> { type Target = SharedContext<'p, 'sess, S>; fn deref(&self) -> &Self::Target { &self.shared } } impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.shared } } impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> { type Target = AttributeParser<'sess, S>; fn deref(&self) -> &Self::Target { self.cx } } impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> { fn deref_mut(&mut self) -> &mut Self::Target { self.cx } } #[derive(PartialEq, Clone, Copy, Debug)] pub enum OmitDoc { Lower, Skip, } #[derive(Copy, Clone)] pub enum ShouldEmit { /// The operation will emit errors and lints. /// This is usually what you need. ErrorsAndLints, /// The operation will emit *not* errors and lints. /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`. Nothing, } impl ShouldEmit { pub fn should_emit(&self) -> bool { match self { ShouldEmit::ErrorsAndLints => true, ShouldEmit::Nothing => false, } } } #[derive(Debug)] pub(crate) enum AllowedTargets { AllowList(&'static [MaybeWarn]), AllowListWarnRest(&'static [MaybeWarn]), } pub(crate) enum AllowedResult { Allowed, Warn, Error, } impl AllowedTargets { pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult { match self { AllowedTargets::AllowList(list) => { if list.contains(&Allow(target)) { AllowedResult::Allowed } else if list.contains(&Warn(target)) { AllowedResult::Warn } else { AllowedResult::Error } } AllowedTargets::AllowListWarnRest(list) => { if list.contains(&Allow(target)) { AllowedResult::Allowed } else if list.contains(&Error(target)) { AllowedResult::Error } else { AllowedResult::Warn } } } } pub(crate) fn allowed_targets(&self) -> Vec { match self { AllowedTargets::AllowList(list) => list, AllowedTargets::AllowListWarnRest(list) => list, } .iter() .filter_map(|target| match target { Allow(target) => Some(*target), Warn(_) => None, Error(_) => None, }) .collect() } } #[derive(Debug, Eq, PartialEq)] pub(crate) enum MaybeWarn { Allow(Target), Warn(Target), Error(Target), } /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess, S: Stage = Late> { pub(crate) tools: Vec, features: Option<&'sess Features>, sess: &'sess Session, stage: S, /// *Only* parse attributes with this symbol. /// /// Used in cases where we want the lowering infrastructure for parse just a single attribute. parse_only: Option, } 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. /// /// Try to use this as little as possible. Attributes *should* be lowered during /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). /// /// 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, target_node_id: NodeId, features: Option<&'sess Features>, ) -> Option { let mut p = Self { features, tools: Vec::new(), parse_only: Some(sym), sess, stage: Early { emit_errors: ShouldEmit::Nothing }, }; let mut parsed = p.parse_attribute_list( attrs, target_span, target_node_id, Target::Crate, // Does not matter, we're not going to emit errors anyways 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 parse_single( sess: &'sess Session, attr: &ast::Attribute, target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, emit_errors: ShouldEmit, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, template: &AttributeTemplate, ) -> T { let mut parser = Self { features, tools: Vec::new(), parse_only: None, sess, stage: Early { emit_errors }, }; let ast::AttrKind::Normal(normal_attr) = &attr.kind else { panic!("parse_single called on a doc attr") }; let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx()); let path = meta_parser.path(); let args = meta_parser.args(); let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { shared: SharedContext { cx: &mut parser, target_span, target_id: target_node_id, emit_lint: &mut |_lint| { panic!("can't emit lints here for now (nothing uses this atm)"); }, }, attr_span: attr.span, template, attr_path: path.get_attribute_path(), }; parse_fn(&mut cx, args) } } impl<'sess, S: Stage> AttributeParser<'sess, S> { pub fn new( sess: &'sess Session, features: &'sess Features, tools: Vec, stage: S, ) -> Self { Self { features: Some(features), tools, parse_only: None, sess, stage } } pub(crate) fn sess(&self) -> &'sess Session { &self.sess } pub(crate) fn features(&self) -> &'sess Features { self.features.expect("features not available at this point in the compiler") } pub(crate) fn features_option(&self) -> Option<&'sess Features> { self.features } pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { 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( &mut self, attrs: &[ast::Attribute], target_span: Span, target_id: S::Id, target: Target, omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, mut emit_lint: impl FnMut(AttributeLint), ) -> Vec { let mut attributes = Vec::new(); let mut attr_paths = Vec::new(); 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 { if !attr.has_name(expected) { continue; } } // Sometimes, for example for `#![doc = include_str!("readme.md")]`, // doc still contains a non-literal. You might say, when we're lowering attributes // that's expanded right? But no, sometimes, when parsing attributes on macros, // we already use the lowering logic and these are still there. So, when `omit_doc` // is set we *also* want to ignore these. if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { continue; } match &attr.kind { ast::AttrKind::DocComment(comment_kind, symbol) => { if omit_doc == OmitDoc::Skip { continue; } attributes.push(Attribute::Parsed(AttributeKind::DocComment { style: attr.style, kind: *comment_kind, span: lower_span(attr.span), comment: *symbol, })) } // // FIXME: make doc attributes go through a proper attribute parser // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => { // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); // // attributes.push(Attribute::Parsed(AttributeKind::DocComment { // style: attr.style, // kind: CommentKind::Line, // span: attr.span, // comment: p.args().name_value(), // })) // } ast::AttrKind::Normal(n) => { attr_paths.push(PathParser::Ast(&n.item.path)); let parser = MetaItemParser::from_attr(n, self.dcx()); let path = parser.path(); let args = parser.args(); let parts = path.segments().map(|i| i.name).collect::>(); if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { for accept in accepts { let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext { shared: SharedContext { cx: self, target_span, target_id, emit_lint: &mut emit_lint, }, attr_span: lower_span(attr.span), template: &accept.template, attr_path: path.get_attribute_path(), }; (accept.accept_fn)(&mut cx, args); if self.stage.should_emit().should_emit() { match accept.allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { let allowed_targets = accept.allowed_targets.allowed_targets(); let (applied, only) = allowed_targets_applied( allowed_targets, target, self.features, ); emit_lint(AttributeLint { id: target_id, span: attr.span, kind: AttributeLintKind::InvalidTarget { name: parts[0], target, only: if only { "only " } else { "" }, applied, }, }); } AllowedResult::Error => { let allowed_targets = accept.allowed_targets.allowed_targets(); let (applied, only) = allowed_targets_applied( allowed_targets, target, self.features, ); self.dcx().emit_err(InvalidTarget { span: attr.span, name: parts[0], target: target.plural_name(), only: if only { "only " } else { "" }, applied, }); } } } } } 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. // If you are that person, and you really think your attribute should // remain unparsed, carefully read the documentation in this module and if // you still think so you can add an exception to this assertion. // FIXME(jdonszelmann): convert other attributes, and check with this that // we caught em all // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; // assert!( // self.tools.contains(&parts[0]) || true, // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), // "attribute {path} wasn't parsed and isn't a know tool attribute", // ); attributes.push(Attribute::Unparsed(Box::new(AttrItem { path: AttrPath::from_ast(&n.item.path), args: self.lower_attr_args(&n.item.args, lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: lower_span(attr.span), }))); } } } } let mut parsed_attributes = Vec::new(); for f in &S::parsers().finalizers { if let Some(attr) = f(&mut FinalizeContext { shared: SharedContext { cx: self, target_span, target_id, emit_lint: &mut emit_lint, }, all_attrs: &attr_paths, }) { parsed_attributes.push(Attribute::Parsed(attr)); } } attributes.extend(parsed_attributes); attributes } /// Returns whether there is a parser for an attribute with this name pub fn is_parsed_attribute(path: &[Symbol]) -> bool { Late::parsers().accepters.contains_key(path) } fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { match args { ast::AttrArgs::Empty => AttrArgs::Empty, ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). ast::AttrArgs::Eq { eq_span, expr } => { // In valid code the value always ends up as a single literal. Otherwise, a dummy // literal suffices because the error is handled elsewhere. let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span)) { lit } else { let guar = self.dcx().span_delayed_bug( args.span().unwrap_or(DUMMY_SP), "expr in place where literal is expected (builtin attr parsing)", ); ast::MetaItemLit { symbol: sym::dummy, suffix: None, kind: ast::LitKind::Err(guar), span: DUMMY_SP, } }; AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit } } } } } /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. /// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string pub(crate) fn allowed_targets_applied( mut allowed_targets: Vec, target: Target, features: Option<&Features>, ) -> (String, bool) { // Remove unstable targets from `allowed_targets` if their features are not enabled if let Some(features) = features { if !features.fn_delegation() { allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. })); } if !features.stmt_expr_attributes() { allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement)); } } // We define groups of "similar" targets. // If at least two of the targets are allowed, and the `target` is not in the group, // we collapse the entire group to a single entry to simplify the target list const FUNCTION_LIKE: &[Target] = &[ Target::Fn, Target::Closure, Target::ForeignFn, Target::Method(MethodKind::Inherent), Target::Method(MethodKind::Trait { body: false }), Target::Method(MethodKind::Trait { body: true }), Target::Method(MethodKind::TraitImpl), ]; const METHOD_LIKE: &[Target] = &[ Target::Method(MethodKind::Inherent), Target::Method(MethodKind::Trait { body: false }), Target::Method(MethodKind::Trait { body: true }), Target::Method(MethodKind::TraitImpl), ]; const IMPL_LIKE: &[Target] = &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }]; const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum]; let mut added_fake_targets = Vec::new(); filter_targets( &mut allowed_targets, FUNCTION_LIKE, "functions", target, &mut added_fake_targets, ); filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets); filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets); filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets); // If there is now only 1 target left, show that as the only possible target ( added_fake_targets .iter() .copied() .chain(allowed_targets.iter().map(|t| t.plural_name())) .join(", "), allowed_targets.len() + added_fake_targets.len() == 1, ) } fn filter_targets( allowed_targets: &mut Vec, target_group: &'static [Target], target_group_name: &'static str, target: Target, added_fake_targets: &mut Vec<&'static str>, ) { if target_group.contains(&target) { return; } if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 { return; } allowed_targets.retain(|t| !target_group.contains(t)); added_fake_targets.push(target_group_name); } /// This is the list of all targets to which a attribute can be applied /// This is used for: /// - `rustc_dummy`, which can be applied to all targets /// - Attributes that are not parted to the new target system yet can use this list as a placeholder pub(crate) const ALL_TARGETS: &'static [MaybeWarn] = &[ Allow(Target::ExternCrate), Allow(Target::Use), Allow(Target::Static), Allow(Target::Const), Allow(Target::Fn), Allow(Target::Closure), Allow(Target::Mod), Allow(Target::ForeignMod), Allow(Target::GlobalAsm), Allow(Target::TyAlias), Allow(Target::Enum), Allow(Target::Variant), Allow(Target::Struct), Allow(Target::Field), Allow(Target::Union), Allow(Target::Trait), Allow(Target::TraitAlias), Allow(Target::Impl { of_trait: false }), Allow(Target::Impl { of_trait: true }), Allow(Target::Expression), Allow(Target::Statement), Allow(Target::Arm), Allow(Target::AssocConst), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::AssocTy), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), Allow(Target::ForeignTy), Allow(Target::MacroDef), Allow(Target::Param), Allow(Target::PatField), Allow(Target::ExprField), Allow(Target::WherePredicate), Allow(Target::MacroCall), Allow(Target::Crate), Allow(Target::Delegation { mac: false }), Allow(Target::Delegation { mac: true }), ]; /// Parse a single integer. /// /// Used by attributes that take a single integer as argument, such as /// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`. /// `cx` is the context given to the attribute. /// `args` is the parser for the attribute arguments. pub(crate) fn parse_single_integer( cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>, ) -> Option { let Some(list) = args.list() else { cx.expected_list(cx.attr_span); return None; }; let Some(single) = list.single() else { cx.expected_single_argument(list.span); return None; }; let Some(lit) = single.lit() else { cx.expected_integer_literal(single.span()); return None; }; let LitKind::Int(num, _ty) = lit.kind else { cx.expected_integer_literal(single.span()); return None; }; Some(num.0) }