use std::cell::RefCell; use std::collections::BTreeMap; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; use rustc_feature::{AttributeTemplate, AttributeType}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::CrateNameParser; 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, LinkParser, 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::prototype::CustomMirParser; 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::parser::{ArgParser, PathParser}; use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; use crate::target_checking::AllowedTargets; type GroupType = LazyLock>; pub(super) struct GroupTypeInner { pub(super) accepters: BTreeMap<&'static [Symbol], Vec>>, pub(super) finalizers: Vec>, } pub(super) struct GroupTypeInnerAccept { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn, pub(super) allowed_targets: AllowedTargets, pub(super) attribute_type: AttributeType, } 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, attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE, }); } 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, 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>, 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; fn id_is_crate_root(id: Self::Id) -> bool; } // 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 { self.should_emit().emit_err(sess.dcx().create_err(diag)) } fn should_emit(&self) -> ShouldEmit { self.emit_errors } fn id_is_crate_root(id: Self::Id) -> bool { id == CRATE_NODE_ID } } // 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 } fn id_is_crate_root(id: Self::Id) -> bool { id == CRATE_HIR_ID } } /// 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, /// Whether it is an inner or outer attribute pub(crate) attr_style: AttrStyle, /// 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 matches!(self.stage.should_emit(), ShouldEmit::Nothing) { 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)) }), }, attr_style: self.attr_style, }) } 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, attr_style: self.attr_style, }) } 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, attr_style: self.attr_style, }) } 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, attr_style: self.attr_style, }) } /// 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, attr_style: self.attr_style, }) } /// 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), attr_style: self.attr_style, }) } /// 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), attr_style: self.attr_style, }) } /// 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, attr_style: self.attr_style, }) } 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, attr_style: self.attr_style, }) } 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, attr_style: self.attr_style, }) } /// produces an error along the lines of `expected one of [foo, meow]` pub(crate) fn expected_specific_argument( &self, span: Span, possibilities: &[Symbol], ) -> 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, }, attr_style: self.attr_style, }) } /// produces an error along the lines of `expected one of [foo, meow] as an argument`. /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument). pub(crate) fn expected_specific_argument_and_list( &self, span: Span, possibilities: &[Symbol], ) -> 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, }, attr_style: self.attr_style, }) } /// produces an error along the lines of `expected one of ["foo", "meow"]` pub(crate) fn expected_specific_argument_strings( &self, span: Span, possibilities: &[Symbol], ) -> 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, }, attr_style: self.attr_style, }) } 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, pub(crate) 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, Debug)] pub enum ShouldEmit { /// The operations will emit errors, and lints, and errors are fatal. /// /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. EarlyFatal, /// 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(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { match self { ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), ShouldEmit::ErrorsAndLints => diag.emit(), ShouldEmit::Nothing => diag.delay_as_bug(), } } }