| 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<S> = LazyLock<GroupTypeInner<S>>; |
| |
| struct GroupTypeInner<S: Stage> { |
| accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>, |
| finalizers: Vec<FinalizeFn<S>>, |
| } |
| |
| struct GroupTypeInnerAccept<S: Stage> { |
| template: AttributeTemplate, |
| accept_fn: AcceptFn<S>, |
| allowed_targets: AllowedTargets, |
| } |
| |
| type AcceptFn<S> = |
| Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>; |
| type FinalizeFn<S> = |
| Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>; |
| |
| macro_rules! attribute_parsers { |
| ( |
| pub(crate) static $name: ident = [$($names: ty),* $(,)?]; |
| ) => { |
| mod early { |
| use super::*; |
| type Combine<T> = super::Combine<T, Early>; |
| type Single<T> = super::Single<T, Early>; |
| type WithoutArgs<T> = super::WithoutArgs<T, Early>; |
| |
| attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];); |
| } |
| mod late { |
| use super::*; |
| type Combine<T> = super::Combine<T, Late>; |
| type Single<T> = super::Single<T, Late>; |
| type WithoutArgs<T> = super::WithoutArgs<T, Late>; |
| |
| 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<GroupTypeInnerAccept<$stage>>>::new(); |
| let mut finalizes = Vec::<FinalizeFn<$stage>>::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<AllowConstFnUnstableParser>, |
| Combine<AllowInternalUnstableParser>, |
| Combine<ReprParser>, |
| Combine<TargetFeatureParser>, |
| Combine<UnstableFeatureBoundParser>, |
| // tidy-alphabetical-end |
| |
| // tidy-alphabetical-start |
| Single<CoverageParser>, |
| Single<DeprecationParser>, |
| Single<DummyParser>, |
| Single<ExportNameParser>, |
| Single<IgnoreParser>, |
| Single<InlineParser>, |
| Single<LinkNameParser>, |
| Single<LinkOrdinalParser>, |
| Single<LinkSectionParser>, |
| Single<LinkageParser>, |
| Single<MustUseParser>, |
| Single<OptimizeParser>, |
| Single<PathAttributeParser>, |
| Single<ProcMacroDeriveParser>, |
| Single<RustcBuiltinMacroParser>, |
| Single<RustcForceInlineParser>, |
| Single<RustcLayoutScalarValidRangeEnd>, |
| Single<RustcLayoutScalarValidRangeStart>, |
| Single<RustcObjectLifetimeDefaultParser>, |
| Single<ShouldPanicParser>, |
| Single<SkipDuringMethodDispatchParser>, |
| Single<TransparencyParser>, |
| Single<WithoutArgs<AllowIncoherentImplParser>>, |
| Single<WithoutArgs<AllowInternalUnsafeParser>>, |
| Single<WithoutArgs<AsPtrParser>>, |
| Single<WithoutArgs<AutomaticallyDerivedParser>>, |
| Single<WithoutArgs<CoherenceIsCoreParser>>, |
| Single<WithoutArgs<CoinductiveParser>>, |
| Single<WithoutArgs<ColdParser>>, |
| Single<WithoutArgs<ConstContinueParser>>, |
| Single<WithoutArgs<ConstStabilityIndirectParser>>, |
| Single<WithoutArgs<ConstTraitParser>>, |
| Single<WithoutArgs<CoroutineParser>>, |
| Single<WithoutArgs<DenyExplicitImplParser>>, |
| Single<WithoutArgs<DoNotImplementViaObjectParser>>, |
| Single<WithoutArgs<ExportStableParser>>, |
| Single<WithoutArgs<FfiConstParser>>, |
| Single<WithoutArgs<FfiPureParser>>, |
| Single<WithoutArgs<FundamentalParser>>, |
| Single<WithoutArgs<LoopMatchParser>>, |
| Single<WithoutArgs<MacroEscapeParser>>, |
| Single<WithoutArgs<MarkerParser>>, |
| Single<WithoutArgs<MayDangleParser>>, |
| Single<WithoutArgs<NoImplicitPreludeParser>>, |
| Single<WithoutArgs<NoMangleParser>>, |
| Single<WithoutArgs<NonExhaustiveParser>>, |
| Single<WithoutArgs<ParenSugarParser>>, |
| Single<WithoutArgs<PassByValueParser>>, |
| Single<WithoutArgs<PointeeParser>>, |
| Single<WithoutArgs<ProcMacroAttributeParser>>, |
| Single<WithoutArgs<ProcMacroParser>>, |
| Single<WithoutArgs<PubTransparentParser>>, |
| Single<WithoutArgs<SpecializationTraitParser>>, |
| Single<WithoutArgs<StdInternalSymbolParser>>, |
| Single<WithoutArgs<TrackCallerParser>>, |
| Single<WithoutArgs<TypeConstParser>>, |
| Single<WithoutArgs<UnsafeSpecializationMarkerParser>>, |
| // 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<Self>; |
| |
| 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<Self> { |
| &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<Self> { |
| &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<Symbol>) -> 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<S::Id>), |
| } |
| |
| /// 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<Target> { |
| 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<Symbol>, |
| 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<Symbol>, |
| } |
| |
| 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<Attribute> { |
| 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<T>( |
| 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<Symbol>, |
| 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<S::Id>), |
| ) -> Vec<Attribute> { |
| 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::<Vec<_>>(); |
| |
| 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: 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>, |
| 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<S: Stage>( |
| cx: &mut AcceptContext<'_, '_, S>, |
| args: &ArgParser<'_>, |
| ) -> Option<u128> { |
| 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) |
| } |