use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;

use private::Sealed;
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
use rustc_errors::{Diag, Diagnostic, Level};
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{AttrPath, HirId};
use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
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::cfi_encoding::CfiEncodingParser;
use crate::attributes::codegen_attrs::{
    ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser,
    NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
    RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
    ThreadLocalParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
    CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
    RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
    WindowsSubsystemParser,
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::do_not_recommend::DoNotRecommendParser;
use crate::attributes::doc::DocParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::instruction_set::InstructionSetParser;
use crate::attributes::link_attrs::{
    ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
    LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser,
};
use crate::attributes::lint_helpers::{
    AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
    RustcShouldNotBeCalledOnConstItems,
};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::macro_attrs::{
    AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser,
    MacroUseParser,
};
use crate::attributes::must_not_suspend::MustNotSuspendParser;
use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::no_link::NoLinkParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::pin_v2::PinV2Parser;
use crate::attributes::proc_macro_attrs::{
    ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_dump::{
    RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs,
    RustcDumpVtable,
};
use crate::attributes::rustc_internal::{
    RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser,
    RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser,
    RustcLintDiagnosticsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser,
    RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser,
    RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser,
    RustcNoImplicitAutorefsParser, RustcObjectLifetimeDefaultParser, RustcScalableVectorParser,
    RustcSimdMonomorphizeLaneLimitParser,
};
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, CoinductiveParser, 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, RefPathParser};
use crate::session_diagnostics::{
    AttributeParseError, AttributeParseErrorReason, ParsedDescription,
};
use crate::target_checking::AllowedTargets;
type GroupType<S> = LazyLock<GroupTypeInner<S>>;

pub(super) struct GroupTypeInner<S: Stage> {
    pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
    pub(super) finalizers: Vec<FinalizeFn<S>>,
}

pub(super) struct GroupTypeInnerAccept<S: Stage> {
    pub(super) template: AttributeTemplate,
    pub(super) accept_fn: AcceptFn<S>,
    pub(super) allowed_targets: AllowedTargets,
}

type AcceptFn<S> =
    Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + 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,
        AlignStaticParser,
        BodyStabilityParser,
        ConfusablesParser,
        ConstStabilityParser,
        DocParser,
        MacroUseParser,
        NakedParser,
        StabilityParser,
        UsedParser,
        // tidy-alphabetical-end

        // tidy-alphabetical-start
        Combine<AllowConstFnUnstableParser>,
        Combine<AllowInternalUnstableParser>,
        Combine<DebuggerViualizerParser>,
        Combine<ForceTargetFeatureParser>,
        Combine<LinkParser>,
        Combine<ReprParser>,
        Combine<TargetFeatureParser>,
        Combine<UnstableFeatureBoundParser>,
        // tidy-alphabetical-end

        // tidy-alphabetical-start
        Single<CfiEncodingParser>,
        Single<CollapseDebugInfoParser>,
        Single<CoverageParser>,
        Single<CrateNameParser>,
        Single<CustomMirParser>,
        Single<DeprecationParser>,
        Single<DoNotRecommendParser>,
        Single<DummyParser>,
        Single<ExportNameParser>,
        Single<IgnoreParser>,
        Single<InlineParser>,
        Single<InstructionSetParser>,
        Single<LinkNameParser>,
        Single<LinkOrdinalParser>,
        Single<LinkSectionParser>,
        Single<LinkageParser>,
        Single<MacroExportParser>,
        Single<MoveSizeLimitParser>,
        Single<MustNotSuspendParser>,
        Single<MustUseParser>,
        Single<ObjcClassParser>,
        Single<ObjcSelectorParser>,
        Single<OptimizeParser>,
        Single<PathAttributeParser>,
        Single<PatternComplexityLimitParser>,
        Single<ProcMacroDeriveParser>,
        Single<RecursionLimitParser>,
        Single<RustcBuiltinMacroParser>,
        Single<RustcForceInlineParser>,
        Single<RustcLayoutScalarValidRangeEndParser>,
        Single<RustcLayoutScalarValidRangeStartParser>,
        Single<RustcLegacyConstGenericsParser>,
        Single<RustcLintOptDenyFieldAccessParser>,
        Single<RustcMustImplementOneOfParser>,
        Single<RustcObjectLifetimeDefaultParser>,
        Single<RustcScalableVectorParser>,
        Single<RustcSimdMonomorphizeLaneLimitParser>,
        Single<SanitizeParser>,
        Single<ShouldPanicParser>,
        Single<SkipDuringMethodDispatchParser>,
        Single<TransparencyParser>,
        Single<TypeLengthLimitParser>,
        Single<WindowsSubsystemParser>,
        Single<WithoutArgs<AllowIncoherentImplParser>>,
        Single<WithoutArgs<AllowInternalUnsafeParser>>,
        Single<WithoutArgs<AsPtrParser>>,
        Single<WithoutArgs<AutomaticallyDerivedParser>>,
        Single<WithoutArgs<CoinductiveParser>>,
        Single<WithoutArgs<ColdParser>>,
        Single<WithoutArgs<ConstContinueParser>>,
        Single<WithoutArgs<ConstStabilityIndirectParser>>,
        Single<WithoutArgs<CoroutineParser>>,
        Single<WithoutArgs<DenyExplicitImplParser>>,
        Single<WithoutArgs<DoNotImplementViaObjectParser>>,
        Single<WithoutArgs<EiiForeignItemParser>>,
        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<NoCoreParser>>,
        Single<WithoutArgs<NoImplicitPreludeParser>>,
        Single<WithoutArgs<NoLinkParser>>,
        Single<WithoutArgs<NoMangleParser>>,
        Single<WithoutArgs<NoStdParser>>,
        Single<WithoutArgs<NonExhaustiveParser>>,
        Single<WithoutArgs<ParenSugarParser>>,
        Single<WithoutArgs<PassByValueParser>>,
        Single<WithoutArgs<PinV2Parser>>,
        Single<WithoutArgs<PointeeParser>>,
        Single<WithoutArgs<ProcMacroAttributeParser>>,
        Single<WithoutArgs<ProcMacroParser>>,
        Single<WithoutArgs<PubTransparentParser>>,
        Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
        Single<WithoutArgs<RustcDumpDefParents>>,
        Single<WithoutArgs<RustcDumpItemBounds>>,
        Single<WithoutArgs<RustcDumpPredicates>>,
        Single<WithoutArgs<RustcDumpUserArgs>>,
        Single<WithoutArgs<RustcDumpVtable>>,
        Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
        Single<WithoutArgs<RustcLintDiagnosticsParser>>,
        Single<WithoutArgs<RustcLintOptTyParser>>,
        Single<WithoutArgs<RustcLintQueryInstabilityParser>>,
        Single<WithoutArgs<RustcLintUntrackedQueryInformationParser>>,
        Single<WithoutArgs<RustcMainParser>>,
        Single<WithoutArgs<RustcNeverReturnsNullPointerParser>>,
        Single<WithoutArgs<RustcNoImplicitAutorefsParser>>,
        Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
        Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
        Single<WithoutArgs<SpecializationTraitParser>>,
        Single<WithoutArgs<StdInternalSymbolParser>>,
        Single<WithoutArgs<ThreadLocalParser>>,
        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 {
        self.should_emit().emit_err(sess.dcx().create_err(diag))
    }

    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 outer span of the attribute currently being parsed
    /// #[attribute(...)]
    /// ^^^^^^^^^^^^^^^^^ outer span
    /// For attributes in `cfg_attr`, the outer span and inner spans are equal.
    pub(crate) attr_span: Span,
    /// The inner span of the attribute currently being parsed
    /// #[attribute(...)]
    ///   ^^^^^^^^^^^^^^  inner span
    pub(crate) inner_span: Span,

    /// Whether it is an inner or outer attribute
    pub(crate) attr_style: AttrStyle,

    /// A description of the thing we are parsing using this attribute parser
    /// We are not only using these parsers for attributes, but also for macros such as the `cfg!()` macro.
    pub(crate) parsed_description: ParsedDescription,

    /// 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: &'static Lint, kind: AttributeLintKind, span: Span) {
        if !matches!(
            self.stage.should_emit(),
            ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
        ) {
            return;
        }
        (self.emit_lint)(LintId::of(lint), span, kind);
    }

    pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
        self.emit_lint(
            rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
            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(
            rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
            AttributeLintKind::UnusedDuplicate {
                this: unused_span,
                other: used_span,
                warning: true,
            },
            unused_span,
        )
    }
}

impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
    fn emit_parse_error(
        &self,
        span: Span,
        reason: AttributeParseErrorReason<'_>,
    ) -> ErrorGuaranteed {
        self.emit_err(AttributeParseError {
            span,
            attr_span: self.attr_span,
            template: self.template.clone(),
            path: self.attr_path.clone(),
            description: self.parsed_description,
            reason,
            suggestions: self.suggestions(),
        })
    }

    /// 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_parse_error(
            span,
            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_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
    }

    pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
        let span = match args {
            ArgParser::NoArgs => span,
            ArgParser::List(list) => list.span,
            ArgParser::NameValue(nv) => nv.args_span(),
        };
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList)
    }

    pub(crate) fn expected_list_with_num_args_or_more(
        &self,
        args: usize,
        span: Span,
    ) -> ErrorGuaranteed {
        self.emit_parse_error(
            span,
            AttributeParseErrorReason::ExpectedListWithNumArgsOrMore { args },
        )
    }

    pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs)
    }

    pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs)
    }

    pub(crate) fn expected_non_empty_string_literal(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNonEmptyStringLiteral)
    }

    pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs)
    }

    /// emit an error that a `name` was expected here
    pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, 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_parse_error(span, 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_parse_error(span, 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_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral)
    }

    pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument)
    }

    pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed {
        self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument)
    }

    /// 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_parse_error(
            span,
            AttributeParseErrorReason::ExpectedSpecificArgument {
                possibilities,
                strings: false,
                list: false,
            },
        )
    }

    /// 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_parse_error(
            span,
            AttributeParseErrorReason::ExpectedSpecificArgument {
                possibilities,
                strings: false,
                list: true,
            },
        )
    }

    /// 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_parse_error(
            span,
            AttributeParseErrorReason::ExpectedSpecificArgument {
                possibilities,
                strings: true,
                list: false,
            },
        )
    }

    pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
        let attr_path = self.attr_path.clone().to_string();
        let valid_without_list = self.template.word;
        self.emit_lint(
            rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
            AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list },
            span,
        );
    }

    pub(crate) fn warn_ill_formed_attribute_input(&mut self, lint: &'static Lint) {
        let suggestions = self.suggestions();
        let span = self.attr_span;
        self.emit_lint(
            lint,
            AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
            span,
        );
    }

    pub(crate) fn suggestions(&self) -> Vec<String> {
        let style = match self.parsed_description {
            // If the outer and inner spans are equal, we are parsing an embedded attribute
            ParsedDescription::Attribute if self.attr_span == self.inner_span => {
                AttrSuggestionStyle::EmbeddedAttribute
            }
            ParsedDescription::Attribute => AttrSuggestionStyle::Attribute(self.attr_style),
            ParsedDescription::Macro => AttrSuggestionStyle::Macro,
        };

        self.template.suggestions(style, &self.attr_path)
    }
}

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,
    pub(crate) target: rustc_hir::Target,

    /// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S`
    /// is `Late` and is the ID of the syntactical component this attribute was applied to.
    pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind),
}

/// 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 [RefPathParser<'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 { also_emit_lints: bool },
    /// 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::ErrorsAndLints`.
    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(),
        }
    }
}
