use std::borrow::Cow;
use std::fmt;
use std::path::PathBuf;

pub use ReprAttr::*;
use rustc_abi::Align;
pub use rustc_ast::attr::data_structures::*;
use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode};
use rustc_ast::expand::typetree::TypeTree;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrStyle, Path, ast};
use rustc_data_structures::fx::FxIndexMap;
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_hir::LangItem;
use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::Transparency;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
pub use rustc_target::spec::SanitizerSet;
use thin_vec::ThinVec;

use crate::attrs::diagnostic::*;
use crate::attrs::pretty_printing::PrintAttribute;
use crate::limit::Limit;
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};

#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub enum EiiImplResolution {
    /// Usually, finding the extern item that an EII implementation implements means finding
    /// the defid of the associated attribute macro, and looking at *its* attributes to find
    /// what foreign item its associated with.
    Macro(DefId),
    /// Sometimes though, we already know statically and can skip some name resolution.
    /// Stored together with the eii's name for diagnostics.
    Known(EiiDecl),
    /// For when resolution failed, but we want to continue compilation
    Error(ErrorGuaranteed),
}

#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub struct EiiImpl {
    pub resolution: EiiImplResolution,
    pub impl_marked_unsafe: bool,
    pub span: Span,
    pub inner_span: Span,
    pub is_default: bool,
}

#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub struct EiiDecl {
    pub foreign_item: DefId,
    /// whether or not it is unsafe to implement this EII
    pub impl_unsafe: bool,
    pub name: Ident,
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, StableHash, PrintAttribute)]
pub enum CguKind {
    No,
    PreDashLto,
    PostDashLto,
    Any,
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, StableHash, PrintAttribute)]
pub enum CguFields {
    PartitionReused { cfg: Symbol, module: Symbol },
    PartitionCodegened { cfg: Symbol, module: Symbol },
    ExpectedCguReuse { cfg: Symbol, module: Symbol, kind: CguKind },
}

#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)]
#[derive(StableHash, Encodable, Decodable)]
pub enum DivergingFallbackBehavior {
    /// Always fallback to `()` (aka "always spontaneous decay")
    ToUnit,
    /// Always fallback to `!` (which should be equivalent to never falling back + not making
    /// never-to-any coercions unless necessary)
    ToNever,
    /// Don't fallback at all
    NoFallback,
}

#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute, Default)]
#[derive(StableHash, Encodable, Decodable)]
pub enum DivergingBlockBehavior {
    /// This is the current stable behavior:
    ///
    /// ```rust
    /// {
    ///     return;
    /// } // block has type = !, even though we are supposedly dropping it with `;`
    /// ```
    #[default]
    Never,

    /// Alternative behavior:
    ///
    /// ```ignore (very-unstable-new-attribute)
    /// #![rustc_never_type_options(diverging_block_default = "unit")]
    /// {
    ///     return;
    /// } // block has type = (), since we are dropping `!` from `return` with `;`
    /// ```
    Unit,
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, StableHash, PrintAttribute)]
pub enum InlineAttr {
    None,
    Hint,
    Always,
    Never,
    /// `#[rustc_force_inline]` forces inlining to happen in the MIR inliner - it reports an error
    /// if the inlining cannot happen. It is limited to only free functions so that the calls
    /// can always be resolved.
    Force {
        attr_span: Span,
        reason: Option<Symbol>,
    },
}

impl InlineAttr {
    pub fn always(&self) -> bool {
        match self {
            InlineAttr::Always | InlineAttr::Force { .. } => true,
            InlineAttr::None | InlineAttr::Hint | InlineAttr::Never => false,
        }
    }
}

#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, StableHash, PrintAttribute)]
pub enum InstructionSetAttr {
    ArmA32,
    ArmT32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
#[derive(Encodable, Decodable, StableHash)]
pub enum OptimizeAttr {
    /// No `#[optimize(..)]` attribute
    #[default]
    Default,
    /// `#[optimize(none)]`
    DoNotOptimize,
    /// `#[optimize(speed)]`
    Speed,
    /// `#[optimize(size)]`
    Size,
}

impl OptimizeAttr {
    pub fn do_not_optimize(&self) -> bool {
        matches!(self, Self::DoNotOptimize)
    }
}

#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, StableHash, PrintAttribute)]
pub enum ReprAttr {
    ReprInt(IntType),
    ReprRust,
    ReprC,
    ReprPacked(Align),
    ReprSimd,
    ReprTransparent,
    ReprAlign(Align),
}

pub enum TransparencyError {
    UnknownTransparency(Symbol, Span),
    MultipleTransparencyAttrs(Span, Span),
}

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable, StableHash, PrintAttribute)]
pub enum IntType {
    SignedInt(ast::IntTy),
    UnsignedInt(ast::UintTy),
}

#[derive(Copy, Debug, Encodable, Decodable, Clone, StableHash, PrintAttribute)]
pub struct Deprecation {
    pub since: DeprecatedSince,
    /// The note to issue a reason.
    pub note: Option<Ident>,
    /// A text snippet used to completely replace any use of the deprecated item in an expression.
    ///
    /// This is currently unstable.
    pub suggestion: Option<Symbol>,
}

/// Release in which an API is deprecated.
#[derive(Copy, Debug, Encodable, Decodable, Clone, StableHash, PrintAttribute)]
pub enum DeprecatedSince {
    RustcVersion(RustcVersion),
    /// Deprecated in the future ("to be determined").
    Future,
    /// `feature(staged_api)` is off. Deprecation versions outside the standard
    /// library are allowed to be arbitrary strings, for better or worse.
    NonStandard(Symbol),
    /// Deprecation version is unspecified but optional.
    Unspecified,
    /// Failed to parse a deprecation version, or the deprecation version is
    /// unspecified and required. An error has already been emitted.
    Err,
}

/// Successfully-parsed value of a `#[coverage(..)]` attribute.
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
#[derive(StableHash, PrintAttribute)]
pub enum CoverageAttrKind {
    On,
    Off,
}

/// Successfully-parsed value of a `#[rustc_abi(..)]` attribute.
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
#[derive(StableHash, PrintAttribute)]
pub enum RustcAbiAttrKind {
    Debug,
    AssertEq,
}

impl Deprecation {
    /// Whether an item marked with #[deprecated(since = "X")] is currently
    /// deprecated (i.e., whether X is not greater than the current rustc
    /// version).
    pub fn is_in_effect(&self) -> bool {
        match self.since {
            DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
            DeprecatedSince::Future => false,
            // The `since` field doesn't have semantic purpose without `#![staged_api]`.
            DeprecatedSince::NonStandard(_) => true,
            // Assume deprecation is in effect if "since" field is absent or invalid.
            DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
        }
    }

    pub fn is_since_rustc_version(&self) -> bool {
        matches!(self.since, DeprecatedSince::RustcVersion(_))
    }
}

/// There are three valid forms of the attribute:
/// `#[used]`, which is equivalent to `#[used(linker)]` on targets that support it, but `#[used(compiler)]` if not.
/// `#[used(compiler)]`
/// `#[used(linker)]`
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(StableHash, PrintAttribute)]
pub enum UsedBy {
    Default,
    Compiler,
    Linker,
}

#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(StableHash, PrintAttribute)]
pub enum MacroUseArgs {
    UseAll,
    UseSpecific(ThinVec<Ident>),
}

impl Default for MacroUseArgs {
    fn default() -> Self {
        Self::UseSpecific(ThinVec::new())
    }
}

#[derive(Debug, Clone, Encodable, Decodable, StableHash)]
pub struct StrippedCfgItem<ScopeId = DefId> {
    pub parent_scope: ScopeId,
    pub ident: Ident,
    pub cfg: (CfgEntry, Span),
}

impl<ScopeId> StrippedCfgItem<ScopeId> {
    pub fn map_scope_id<New>(self, f: impl FnOnce(ScopeId) -> New) -> StrippedCfgItem<New> {
        StrippedCfgItem { parent_scope: f(self.parent_scope), ident: self.ident, cfg: self.cfg }
    }
}

/// Possible values for the `#[linkage]` attribute, allowing to specify the
/// linkage type for a `MonoItem`.
///
/// See <https://llvm.org/docs/LangRef.html#linkage-types> for more details about these variants.
#[derive(Encodable, Decodable, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(StableHash, PrintAttribute)]
pub enum Linkage {
    AvailableExternally,
    Common,
    ExternalWeak,
    External,
    Internal,
    LinkOnceAny,
    LinkOnceODR,
    WeakAny,
    WeakODR,
}

#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
#[derive(StableHash, PrintAttribute)]
pub enum MirDialect {
    Analysis,
    Built,
    Runtime,
}

impl IntoDiagArg for MirDialect {
    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
        let arg = match self {
            MirDialect::Analysis => "analysis",
            MirDialect::Built => "built",
            MirDialect::Runtime => "runtime",
        };
        DiagArgValue::Str(Cow::Borrowed(arg))
    }
}

#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
#[derive(StableHash, PrintAttribute)]
pub enum MirPhase {
    Initial,
    PostCleanup,
    Optimized,
}

impl IntoDiagArg for MirPhase {
    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
        let arg = match self {
            MirPhase::Initial => "initial",
            MirPhase::PostCleanup => "post-cleanup",
            MirPhase::Optimized => "optimized",
        };
        DiagArgValue::Str(Cow::Borrowed(arg))
    }
}

/// Different ways that the PE Format can decorate a symbol name.
/// From <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-name-type>
#[derive(Copy, Clone, Debug, Encodable, Decodable, StableHash, PartialEq, Eq, PrintAttribute)]
pub enum PeImportNameType {
    /// IMPORT_ORDINAL
    /// Uses the ordinal (i.e., a number) rather than the name.
    Ordinal(u16),
    /// Same as IMPORT_NAME
    /// Name is decorated with all prefixes and suffixes.
    Decorated,
    /// Same as IMPORT_NAME_NOPREFIX
    /// Prefix (e.g., the leading `_` or `@`) is skipped, but suffix is kept.
    NoPrefix,
    /// Same as IMPORT_NAME_UNDECORATE
    /// Prefix (e.g., the leading `_` or `@`) and suffix (the first `@` and all
    /// trailing characters) are skipped.
    Undecorated,
}

#[derive(
    Copy,
    Clone,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Encodable,
    Decodable,
    PrintAttribute
)]
#[derive(StableHash)]
pub enum NativeLibKind {
    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC)
    Static {
        /// Whether to bundle objects from static library into produced rlib
        bundle: Option<bool>,
        /// Whether to link static library without throwing any object files away
        whole_archive: Option<bool>,
        /// Whether to export c static library symbols
        export_symbols: Option<bool>,
    },
    /// Dynamic library (e.g. `libfoo.so` on Linux)
    /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC).
    Dylib {
        /// Whether the dynamic library will be linked only if it satisfies some undefined symbols
        as_needed: Option<bool>,
    },
    /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library.
    /// On Linux, it refers to a generated shared library stub.
    RawDylib {
        /// Whether the dynamic library will be linked only if it satisfies some undefined symbols
        as_needed: Option<bool>,
    },
    /// A macOS-specific kind of dynamic libraries.
    Framework {
        /// Whether the framework will be linked only if it satisfies some undefined symbols
        as_needed: Option<bool>,
    },
    /// Argument which is passed to linker, relative order with libraries and other arguments
    /// is preserved
    LinkArg,

    /// Module imported from WebAssembly
    WasmImportModule,

    /// The library kind wasn't specified, `Dylib` is currently used as a default.
    Unspecified,
}

impl NativeLibKind {
    pub fn has_modifiers(&self) -> bool {
        match self {
            NativeLibKind::Static { bundle, whole_archive, export_symbols } => {
                bundle.is_some() || whole_archive.is_some() || export_symbols.is_some()
            }
            NativeLibKind::Dylib { as_needed }
            | NativeLibKind::Framework { as_needed }
            | NativeLibKind::RawDylib { as_needed } => as_needed.is_some(),
            NativeLibKind::Unspecified
            | NativeLibKind::LinkArg
            | NativeLibKind::WasmImportModule => false,
        }
    }

    pub fn is_statically_included(&self) -> bool {
        matches!(self, NativeLibKind::Static { .. })
    }

    pub fn is_dllimport(&self) -> bool {
        matches!(
            self,
            NativeLibKind::Dylib { .. }
                | NativeLibKind::RawDylib { .. }
                | NativeLibKind::Unspecified
        )
    }
}

#[derive(Debug, Encodable, Decodable, Clone, StableHash, PrintAttribute)]
pub struct LinkEntry {
    pub span: Span,
    pub kind: NativeLibKind,
    pub name: Symbol,
    pub cfg: Option<CfgEntry>,
    pub verbatim: Option<bool>,
    pub import_name_type: Option<(PeImportNameType, Span)>,
}

#[derive(StableHash, PrintAttribute)]
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
pub enum DebuggerVisualizerType {
    Natvis,
    GdbPrettyPrinter,
}

#[derive(Debug, Encodable, Decodable, Clone, StableHash, PrintAttribute)]
pub struct DebugVisualizer {
    pub span: Span,
    pub visualizer_type: DebuggerVisualizerType,
    pub path: Symbol,
}

#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[derive(StableHash, PrintAttribute)]
#[derive_const(Default)]
pub enum RtsanSetting {
    Nonblocking,
    Blocking,
    #[default]
    Caller,
}

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[derive(Encodable, Decodable, StableHash, PrintAttribute)]
pub enum WindowsSubsystemKind {
    Console,
    Windows,
}

impl WindowsSubsystemKind {
    pub fn as_str(&self) -> &'static str {
        match self {
            WindowsSubsystemKind::Console => "console",
            WindowsSubsystemKind::Windows => "windows",
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub enum DocInline {
    Inline,
    NoInline,
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub enum HideOrShow {
    Hide,
    Show,
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub struct CfgInfo {
    pub name: Symbol,
    pub name_span: Span,
    pub value: Option<(Symbol, Span)>,
}

impl CfgInfo {
    pub fn span_for_name_and_value(&self) -> Span {
        if let Some((_, value_span)) = self.value {
            self.name_span.with_hi(value_span.hi())
        } else {
            self.name_span
        }
    }
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub struct CfgHideShow {
    pub kind: HideOrShow,
    pub values: ThinVec<CfgInfo>,
}

#[derive(Clone, Debug, Default, StableHash, Decodable, PrintAttribute)]
pub struct DocAttribute {
    pub first_span: Span,

    pub aliases: FxIndexMap<Symbol, Span>,
    pub hidden: Option<Span>,
    // Because we need to emit the error if there is more than one `inline` attribute on an item
    // at the same time as the other doc attributes, we store a list instead of using `Option`.
    pub inline: ThinVec<(DocInline, Span)>,

    // unstable
    pub cfg: ThinVec<CfgEntry>,
    pub auto_cfg: ThinVec<(CfgHideShow, Span)>,
    /// This is for `#[doc(auto_cfg = false|true)]`/`#[doc(auto_cfg)]`.
    pub auto_cfg_change: ThinVec<(bool, Span)>,

    // builtin
    pub fake_variadic: Option<Span>,
    pub keyword: Option<(Symbol, Span)>,
    pub attribute: Option<(Symbol, Span)>,
    pub masked: Option<Span>,
    pub notable_trait: Option<Span>,
    pub search_unbox: Option<Span>,

    // valid on crate
    pub html_favicon_url: Option<(Symbol, Span)>,
    pub html_logo_url: Option<(Symbol, Span)>,
    pub html_playground_url: Option<(Symbol, Span)>,
    pub html_root_url: Option<(Symbol, Span)>,
    pub html_no_source: Option<Span>,
    pub issue_tracker_base_url: Option<(Symbol, Span)>,
    pub rust_logo: Option<Span>,

    // #[doc(test(...))]
    pub test_attrs: ThinVec<Span>,
    pub no_crate_inject: Option<Span>,
}

impl<E: rustc_span::SpanEncoder> rustc_serialize::Encodable<E> for DocAttribute {
    fn encode(&self, encoder: &mut E) {
        let DocAttribute {
            first_span,
            aliases,
            hidden,
            inline,
            cfg,
            auto_cfg,
            auto_cfg_change,
            fake_variadic,
            keyword,
            attribute,
            masked,
            notable_trait,
            search_unbox,
            html_favicon_url,
            html_logo_url,
            html_playground_url,
            html_root_url,
            html_no_source,
            issue_tracker_base_url,
            rust_logo,
            test_attrs,
            no_crate_inject,
        } = self;
        rustc_serialize::Encodable::<E>::encode(first_span, encoder);
        rustc_serialize::Encodable::<E>::encode(aliases, encoder);
        rustc_serialize::Encodable::<E>::encode(hidden, encoder);

        // FIXME: The `doc(inline)` attribute is never encoded, but is it actually the right thing
        // to do? I suspect the condition was broken, should maybe instead not encode anything if we
        // have `doc(no_inline)`.
        let inline: ThinVec<_> =
            inline.iter().filter(|(i, _)| *i != DocInline::Inline).cloned().collect();
        rustc_serialize::Encodable::<E>::encode(&inline, encoder);

        rustc_serialize::Encodable::<E>::encode(cfg, encoder);
        rustc_serialize::Encodable::<E>::encode(auto_cfg, encoder);
        rustc_serialize::Encodable::<E>::encode(auto_cfg_change, encoder);
        rustc_serialize::Encodable::<E>::encode(fake_variadic, encoder);
        rustc_serialize::Encodable::<E>::encode(keyword, encoder);
        rustc_serialize::Encodable::<E>::encode(attribute, encoder);
        rustc_serialize::Encodable::<E>::encode(masked, encoder);
        rustc_serialize::Encodable::<E>::encode(notable_trait, encoder);
        rustc_serialize::Encodable::<E>::encode(search_unbox, encoder);
        rustc_serialize::Encodable::<E>::encode(html_favicon_url, encoder);
        rustc_serialize::Encodable::<E>::encode(html_logo_url, encoder);
        rustc_serialize::Encodable::<E>::encode(html_playground_url, encoder);
        rustc_serialize::Encodable::<E>::encode(html_root_url, encoder);
        rustc_serialize::Encodable::<E>::encode(html_no_source, encoder);
        rustc_serialize::Encodable::<E>::encode(issue_tracker_base_url, encoder);
        rustc_serialize::Encodable::<E>::encode(rust_logo, encoder);
        rustc_serialize::Encodable::<E>::encode(test_attrs, encoder);
        rustc_serialize::Encodable::<E>::encode(no_crate_inject, encoder);
    }
}

/// How to perform collapse macros debug info
/// if-ext - if macro from different crate (related to callsite code)
/// | cmd \ attr    | no  | (unspecified) | external | yes |
/// | no            | no  | no            | no       | no  |
/// | (unspecified) | no  | no            | if-ext   | yes |
/// | external      | no  | if-ext        | if-ext   | yes |
/// | yes           | yes | yes           | yes      | yes |
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub enum CollapseMacroDebuginfo {
    /// Don't collapse debuginfo for the macro
    No = 0,
    /// Unspecified value
    Unspecified = 1,
    /// Collapse debuginfo if the macro comes from a different crate
    External = 2,
    /// Collapse debuginfo for the macro
    Yes = 3,
}

/// Crate type, as specified by `#![crate_type]`
#[derive(Copy, Clone, Debug, Hash, PartialEq, Default, PartialOrd, Eq, Ord)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub enum CrateType {
    /// `#![crate_type = "bin"]`
    Executable,
    /// `#![crate_type = "dylib"]`
    Dylib,
    /// `#![crate_type = "rlib"]` or `#![crate_type = "lib"]`
    #[default]
    Rlib,
    /// `#![crate_type = "staticlib"]`
    StaticLib,
    /// `#![crate_type = "cdylib"]`
    Cdylib,
    /// `#![crate_type = "proc-macro"]`
    ProcMacro,
    /// `#![crate_type = "sdylib"]`
    // Unstable; feature(export_stable)
    Sdylib,
}

impl CrateType {
    /// Pairs of each `#[crate_type] = "..."` value and the crate type it resolves to
    pub fn all() -> &'static [(Symbol, Self)] {
        debug_assert_eq!(CrateType::default(), CrateType::Rlib);
        &[
            (rustc_span::sym::lib, CrateType::Rlib),
            (rustc_span::sym::rlib, CrateType::Rlib),
            (rustc_span::sym::dylib, CrateType::Dylib),
            (rustc_span::sym::cdylib, CrateType::Cdylib),
            (rustc_span::sym::staticlib, CrateType::StaticLib),
            (rustc_span::sym::proc_dash_macro, CrateType::ProcMacro),
            (rustc_span::sym::bin, CrateType::Executable),
            (rustc_span::sym::sdylib, CrateType::Sdylib),
        ]
    }

    /// Same as [`CrateType::all`], but does not include unstable options.
    /// Used for diagnostics.
    pub fn all_stable() -> &'static [(Symbol, Self)] {
        debug_assert_eq!(CrateType::default(), CrateType::Rlib);
        &[
            (rustc_span::sym::lib, CrateType::Rlib),
            (rustc_span::sym::rlib, CrateType::Rlib),
            (rustc_span::sym::dylib, CrateType::Dylib),
            (rustc_span::sym::cdylib, CrateType::Cdylib),
            (rustc_span::sym::staticlib, CrateType::StaticLib),
            (rustc_span::sym::proc_dash_macro, CrateType::ProcMacro),
            (rustc_span::sym::bin, CrateType::Executable),
        ]
    }

    pub fn has_metadata(self) -> bool {
        match self {
            CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
            CrateType::Executable
            | CrateType::Cdylib
            | CrateType::StaticLib
            | CrateType::Sdylib => false,
        }
    }
}

impl TryFrom<Symbol> for CrateType {
    type Error = ();

    fn try_from(value: Symbol) -> Result<Self, Self::Error> {
        Ok(match value {
            rustc_span::sym::bin => CrateType::Executable,
            rustc_span::sym::dylib => CrateType::Dylib,
            rustc_span::sym::staticlib => CrateType::StaticLib,
            rustc_span::sym::cdylib => CrateType::Cdylib,
            rustc_span::sym::rlib => CrateType::Rlib,
            rustc_span::sym::lib => CrateType::default(),
            rustc_span::sym::proc_dash_macro => CrateType::ProcMacro,
            rustc_span::sym::sdylib => CrateType::Sdylib,
            _ => return Err(()),
        })
    }
}

impl std::fmt::Display for CrateType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match *self {
            CrateType::Executable => "bin".fmt(f),
            CrateType::Dylib => "dylib".fmt(f),
            CrateType::Rlib => "rlib".fmt(f),
            CrateType::StaticLib => "staticlib".fmt(f),
            CrateType::Cdylib => "cdylib".fmt(f),
            CrateType::ProcMacro => "proc-macro".fmt(f),
            CrateType::Sdylib => "sdylib".fmt(f),
        }
    }
}

impl IntoDiagArg for CrateType {
    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
        self.to_string().into_diag_arg(&mut None)
    }
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub enum RustcDumpLayoutKind {
    Align,
    BackendRepr,
    Debug,
    HomogenousAggregate,
    Size,
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)]
pub enum RustcMirKind {
    PeekMaybeInit,
    PeekMaybeUninit,
    PeekLiveness,
    StopAfterDataflow,
    BorrowckGraphvizPostflow { path: PathBuf },
    BorrowckGraphvizFormat { format: BorrowckGraphvizFormatKind },
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)]
pub enum BorrowckGraphvizFormatKind {
    TwoPhase,
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub struct RustcCleanAttribute {
    pub span: Span,
    pub cfg: Symbol,
    pub except: Option<RustcCleanQueries>,
    pub loaded_from_disk: Option<RustcCleanQueries>,
}

/// Represents the `except=` or `loaded_from_disk=` argument of `#[rustc_clean]`
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub struct RustcCleanQueries {
    pub entries: ThinVec<Symbol>,
    pub span: Span,
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(StableHash, Encodable, Decodable, PrintAttribute)]
pub struct RustcAutodiff {
    /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and
    /// e.g. in the [JAX
    /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions).
    pub mode: DiffMode,
    /// A user-provided, batching width. If not given, we will default to 1 (no batching).
    /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to:
    /// - Calling the function 50 times with a batch size of 2
    /// - Calling the function 25 times with a batch size of 4,
    /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from
    /// cache locality, better re-usal of primal values, and other optimizations.
    /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width`
    /// times, so this massively increases code size. As such, values like 1024 are unlikely to
    /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for
    /// experiments for now and focus on documenting the implications of a large width.
    pub width: u32,
    pub input_activity: ThinVec<DiffActivity>,
    pub ret_activity: DiffActivity,
}

impl RustcAutodiff {
    pub fn has_primal_ret(&self) -> bool {
        matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual)
    }
}

impl RustcAutodiff {
    pub fn has_ret_activity(&self) -> bool {
        self.ret_activity != DiffActivity::None
    }
    pub fn has_active_only_ret(&self) -> bool {
        self.ret_activity == DiffActivity::ActiveOnly
    }

    pub fn error() -> Self {
        RustcAutodiff {
            mode: DiffMode::Error,
            width: 0,
            ret_activity: DiffActivity::None,
            input_activity: ThinVec::new(),
        }
    }

    pub fn source() -> Self {
        RustcAutodiff {
            mode: DiffMode::Source,
            width: 0,
            ret_activity: DiffActivity::None,
            input_activity: ThinVec::new(),
        }
    }

    pub fn is_active(&self) -> bool {
        self.mode != DiffMode::Error
    }

    pub fn is_source(&self) -> bool {
        self.mode == DiffMode::Source
    }
    pub fn apply_autodiff(&self) -> bool {
        !matches!(self.mode, DiffMode::Error | DiffMode::Source)
    }

    pub fn into_item(
        self,
        source: String,
        target: String,
        inputs: Vec<TypeTree>,
        output: TypeTree,
    ) -> AutoDiffItem {
        AutoDiffItem { source, target, inputs, output, attrs: self }
    }
}

/// We generate one of these structs for each `#[autodiff(...)]` attribute.
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, StableHash)]
pub struct AutoDiffItem {
    /// The name of the function getting differentiated
    pub source: String,
    /// The name of the function being generated
    pub target: String,
    pub attrs: RustcAutodiff,
    pub inputs: Vec<TypeTree>,
    pub output: TypeTree,
}

impl fmt::Display for AutoDiffItem {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Differentiating {} -> {}", self.source, self.target)?;
        write!(f, " with attributes: {:?}", self.attrs)?;
        write!(f, " with inputs: {:?}", self.inputs)?;
        write!(f, " with output: {:?}", self.output)
    }
}

#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub struct UnstableRemovedFeature {
    pub feature: Symbol,
    pub reason: Symbol,
    pub link: Symbol,
    pub since: RustcVersion,
}

/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
/// These attributes are markers that guide the compilation process and are never expanded into other code.
/// They persist throughout the compilation phases, from AST to HIR and beyond.
///
/// ## Attribute Processing
/// While attributes are initially parsed by [`rustc_parse`] into [`ast::Attribute`], they still contain raw token streams
/// because different attributes have different internal structures. This enum represents the final,
/// fully parsed form of these attributes, where each variant contains all the information and
/// structure relevant for the specific attribute.
///
/// Some attributes can be applied multiple times to the same item, and they are "collapsed" into a single
/// semantic attribute. For example:
/// ```rust
/// #[repr(C)]
/// #[repr(packed)]
/// struct S { }
/// ```
/// This is equivalent to `#[repr(C, packed)]` and results in a single [`AttributeKind::Repr`] containing
/// both `C` and `packed` annotations. This collapsing happens during parsing and is reflected in the
/// data structures defined in this enum.
///
/// ## Usage
/// These parsed attributes are used throughout the compiler to:
/// - Control code generation (e.g., `#[repr]`)
/// - Mark API stability (`#[stable]`, `#[unstable]`)
/// - Provide documentation (`#[doc]`)
/// - Guide compiler behavior (e.g., `#[allow_internal_unstable]`)
///
/// ## Note on Attribute Organization
/// Some attributes like `InlineAttr`, `OptimizeAttr`, and `InstructionSetAttr` are defined separately
/// from this enum because they are used in specific compiler phases (like code generation) and don't
/// need to persist throughout the entire compilation process. They are typically processed and
/// converted into their final form earlier in the compilation pipeline.
///
/// For example:
/// - `InlineAttr` is used during code generation to control function inlining
/// - `OptimizeAttr` is used to control optimization levels
/// - `InstructionSetAttr` is used for target-specific code generation
///
/// These attributes are handled by their respective compiler passes in the [`rustc_codegen_ssa`] crate
/// and don't need to be preserved in the same way as the attributes in this enum.
///
/// For more details on attribute parsing, see the [`rustc_attr_parsing`] crate.
///
/// [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
/// [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
pub enum AttributeKind {
    // tidy-alphabetical-start
    /// Represents `#[allow_internal_unsafe]`.
    AllowInternalUnsafe(Span),

    /// Represents `#[allow_internal_unstable]`.
    AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),

    /// Represents `#[automatically_derived]`
    AutomaticallyDerived,

    /// Represents the trace attribute of `#[cfg_attr]`
    CfgAttrTrace,

    /// Represents the trace attribute of `#[cfg]`
    CfgTrace(ThinVec<(CfgEntry, Span)>),

    /// Represents `#[cfi_encoding]`
    CfiEncoding {
        encoding: Symbol,
    },

    /// Represents `#[cold]`.
    Cold,

    /// Represents `#[collapse_debuginfo]`.
    CollapseDebugInfo(CollapseMacroDebuginfo),

    /// Represents `#[compiler_builtins]`.
    CompilerBuiltins,

    /// Represents `#[const_continue]`.
    ConstContinue(Span),

    /// Represents `#[coroutine]`.
    Coroutine,

    /// Represents `#[coverage(..)]`.
    Coverage(CoverageAttrKind),

    /// Represents `#[crate_name = ...]`
    CrateName {
        name: Symbol,
        name_span: Span,
        attr_span: Span,
    },

    /// Represents `#![crate_type = ...]`
    CrateType(ThinVec<CrateType>),

    /// Represents `#[custom_mir]`.
    CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>),

    /// Represents `#[debugger_visualizer]`.
    DebuggerVisualizer(ThinVec<DebugVisualizer>),

    /// Represents `#![default_lib_allocator]`
    DefaultLibAllocator,

    /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
    Deprecated {
        deprecation: Deprecation,
        span: Span,
    },

    /// Represents `#[diagnostic::do_not_recommend]`.
    DoNotRecommend,

    /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
    /// Represents all other uses of the [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html)
    /// attribute.
    Doc(Box<DocAttribute>),

    /// Represents specifically [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
    /// i.e. doc comments.
    DocComment {
        style: AttrStyle,
        kind: DocFragmentKind,
        span: Span,
        comment: Symbol,
    },

    /// Implementation detail of `#[eii]`
    EiiDeclaration(EiiDecl),

    /// Implementation detail of `#[eii]`
    EiiImpls(ThinVec<EiiImpl>),

    /// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute).
    ExportName {
        /// The name to export this item with.
        /// It may not contain \0 bytes as it will be converted to a null-terminated string.
        name: Symbol,
        span: Span,
    },

    /// Represents `#[export_stable]`.
    ExportStable,

    /// Represents `#[feature(...)]`
    Feature(ThinVec<Ident>, Span),

    /// Represents `#[ffi_const]`.
    FfiConst,

    /// Represents `#[ffi_pure]`.
    FfiPure(Span),

    /// Represents `#[fundamental]`.
    Fundamental,

    /// Represents `#[ignore]`
    Ignore {
        span: Span,
        /// ignore can optionally have a reason: `#[ignore = "reason this is ignored"]`
        reason: Option<Symbol>,
    },

    /// Represents `#[inline]` and `#[rustc_force_inline]`.
    Inline(InlineAttr, Span),

    /// Represents `#[instruction_set]`
    InstructionSet(InstructionSetAttr),

    /// Represents `#[lang]`
    Lang(LangItem),

    /// Represents `#[link]`.
    Link(ThinVec<LinkEntry>, Span),

    /// Represents `#[link_name]`.
    LinkName {
        name: Symbol,
        span: Span,
    },

    /// Represents `#[link_ordinal]`.
    LinkOrdinal {
        ordinal: u16,
        span: Span,
    },

    /// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
    LinkSection {
        name: Symbol,
    },

    /// Represents `#[linkage]`.
    Linkage(Linkage, Span),

    /// Represents `#[loop_match]`.
    LoopMatch(Span),

    /// Represents `#[macro_escape]`.
    MacroEscape,

    /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path).
    MacroExport {
        span: Span,
        local_inner_macros: bool,
    },

    /// Represents `#[macro_use]`.
    MacroUse {
        span: Span,
        arguments: MacroUseArgs,
    },

    /// Represents `#[marker]`.
    Marker,

    /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
    MayDangle(Span),

    /// Represents `#[move_size_limit]`
    MoveSizeLimit {
        limit: Limit,
    },

    /// Represents `#[must_not_suspend]`
    MustNotSupend {
        reason: Option<Symbol>,
    },

    /// Represents `#[must_use]`.
    MustUse {
        /// Used by `clippy`.
        span: Span,
        /// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
        reason: Option<Symbol>,
    },

    /// Represents `#[naked]`
    Naked(Span),

    /// Represents `#[needs_allocator]`
    NeedsAllocator,

    /// Represents `#[needs_panic_runtime]`
    NeedsPanicRuntime,

    /// Represents `#[no_builtins]`
    NoBuiltins,

    /// Represents `#[no_core]`
    NoCore,

    /// Represents `#[no_implicit_prelude]`
    NoImplicitPrelude,

    /// Represents `#[no_link]`
    NoLink,

    /// Represents `#[no_main]`
    NoMain,

    /// Represents `#[no_mangle]`
    NoMangle(Span),

    /// Represents `#[no_std]`
    NoStd,

    /// Represents `#[non_exhaustive]`
    NonExhaustive(Span),

    /// Represents `#[diagnostic::on_const]`.
    OnConst {
        span: Span,
        /// None if the directive was malformed in some way.
        directive: Option<Box<Directive>>,
    },

    /// Represents `#[diagnostic::on_move]`
    OnMove {
        directive: Option<Box<Directive>>,
    },

    /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`.
    OnUnimplemented {
        /// None if the directive was malformed in some way.
        directive: Option<Box<Directive>>,
    },

    /// Represents `#[diagnostic::on_unknown]`
    OnUnknown {
        /// None if the directive was malformed in some way.
        directive: Option<Box<Directive>>,
    },

    /// Represents `#[diagnostic::on_unmatch_args]`.
    OnUnmatchArgs {
        /// None if the directive was malformed in some way.
        directive: Option<Box<Directive>>,
    },

    /// Represents `#[optimize(size|speed)]`
    Optimize(OptimizeAttr, Span),

    /// Represents `#[panic_runtime]`
    PanicRuntime,

    /// Represents `#[patchable_function_entry]`
    PatchableFunctionEntry {
        prefix: u8,
        entry: u8,
    },

    /// Represents `#[path]`
    Path(Symbol),

    /// Represents `#[pattern_complexity_limit]`
    PatternComplexityLimit {
        limit: Limit,
    },

    /// Represents `#[pin_v2]`
    PinV2(Span),

    /// Represents `#[prelude_import]`
    PreludeImport,

    /// Represents `#[proc_macro]`
    ProcMacro,

    /// Represents `#[proc_macro_attribute]`
    ProcMacroAttribute,

    /// Represents `#[proc_macro_derive]`
    ProcMacroDerive {
        trait_name: Symbol,
        helper_attrs: ThinVec<Symbol>,
    },

    /// Represents `#[profiler_runtime]`
    ProfilerRuntime,

    /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)
    RecursionLimit {
        limit: Limit,
    },

    /// Represents `#[reexport_test_harness_main]`
    ReexportTestHarnessMain(Symbol),

    /// Represents `#[register_tool]`
    RegisterTool(ThinVec<Ident>),

    /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
    Repr {
        reprs: ThinVec<(ReprAttr, Span)>,
        first_span: Span,
    },

    /// Represents `#[rustc_abi(..)]`
    RustcAbi {
        attr_span: Span,
        kind: RustcAbiAttrKind,
    },

    /// Represents `#[align(N)]`.
    // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
    RustcAlign {
        align: Align,
        span: Span,
    },

    /// Represents `#[rustc_allocator]`
    RustcAllocator,

    /// Represents `#[rustc_allocator_zeroed]`
    RustcAllocatorZeroed,

    /// Represents `#[rustc_allocator_zeroed_variant]`
    RustcAllocatorZeroedVariant {
        name: Symbol,
    },

    /// Represents `#[rustc_allow_const_fn_unstable]`.
    RustcAllowConstFnUnstable(ThinVec<Symbol>, Span),

    /// Represents `#[rustc_allow_incoherent_impl]`.
    RustcAllowIncoherentImpl(Span),

    /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
    RustcAsPtr,

    /// Represents `#[rustc_autodiff]`.
    RustcAutodiff(Option<Box<RustcAutodiff>>),

    /// Represents `#[rustc_default_body_unstable]`.
    RustcBodyStability {
        stability: DefaultBodyStability,
        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
        span: Span,
    },
    /// Represents `#[rustc_builtin_macro]`.
    RustcBuiltinMacro {
        builtin_name: Option<Symbol>,
        helper_attrs: ThinVec<Symbol>,
    },
    /// Represents `#[rustc_capture_analysis]`
    RustcCaptureAnalysis,

    /// Represents `#[rustc_expected_cgu_reuse]`, `#[rustc_partition_codegened]` and `#[rustc_partition_reused]`.
    RustcCguTestAttr(ThinVec<(Span, CguFields)>),

    /// Represents `#[rustc_clean]`
    RustcClean(ThinVec<RustcCleanAttribute>),

    /// Represents `#[rustc_coherence_is_core]`
    RustcCoherenceIsCore,

    /// Represents `#[rustc_coinductive]`.
    RustcCoinductive,

    /// Represents `#[rustc_comptime]`
    RustcComptime(Span),

    /// Represents `#[rustc_confusables]`.
    RustcConfusables {
        confusables: ThinVec<Symbol>,
    },
    /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
    RustcConstStability {
        stability: PartialConstStability,
        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
        span: Span,
    },

    /// Represents `#[rustc_const_stable_indirect]`.
    RustcConstStableIndirect,

    /// Represents `#[rustc_conversion_suggestion]`
    RustcConversionSuggestion,

    /// Represents `#[rustc_deallocator]`
    RustcDeallocator,

    /// Represents `#[rustc_delayed_bug_from_inside_query]`
    RustcDelayedBugFromInsideQuery,

    /// Represents `#[rustc_deny_explicit_impl]`.
    RustcDenyExplicitImpl,

    /// Represents `#[rustc_deprecated_safe_2024]`
    RustcDeprecatedSafe2024 {
        suggestion: Symbol,
    },
    /// Represents `#[rustc_diagnostic_item]`
    RustcDiagnosticItem(Symbol),

    /// Represents `#[rustc_do_not_const_check]`
    RustcDoNotConstCheck,

    /// Represents `#[rustc_doc_primitive = ...]`
    RustcDocPrimitive(Span, Symbol),

    /// Represents `#[rustc_dummy]`.
    RustcDummy,

    /// Represents `#[rustc_dump_def_parents]`
    RustcDumpDefParents,

    /// Represents `#[rustc_dump_def_path]`
    RustcDumpDefPath(Span),

    /// Represents `#[rustc_dump_hidden_type_of_opaques]`
    RustcDumpHiddenTypeOfOpaques,

    /// Represents `#[rustc_dump_inferred_outlives]`
    RustcDumpInferredOutlives,

    /// Represents `#[rustc_dump_item_bounds]`
    RustcDumpItemBounds,

    /// Represents `#[rustc_dump_layout]`
    RustcDumpLayout(ThinVec<RustcDumpLayoutKind>),

    /// Represents `#[rustc_dump_object_lifetime_defaults]`.
    RustcDumpObjectLifetimeDefaults,

    /// Represents `#[rustc_dump_predicates]`
    RustcDumpPredicates,

    /// Represents `#[rustc_dump_symbol_name]`
    RustcDumpSymbolName(Span),

    /// Represents `#[rustc_dump_user_args]`
    RustcDumpUserArgs,

    /// Represents `#[rustc_dump_variances]`
    RustcDumpVariances,

    /// Represents `#[rustc_dump_variances_of_opaques]`
    RustcDumpVariancesOfOpaques,

    /// Represents `#[rustc_dump_vtable]`
    RustcDumpVtable(Span),

    /// Represents `#[rustc_dyn_incompatible_trait]`.
    RustcDynIncompatibleTrait(Span),

    /// Represents `#[rustc_effective_visibility]`.
    RustcEffectiveVisibility,

    /// Implementation detail of `#[eii]`
    RustcEiiForeignItem,

    /// Represents `#[rustc_evaluate_where_clauses]`
    RustcEvaluateWhereClauses,

    RustcHasIncoherentInherentImpls,

    /// Represents `#[rustc_if_this_changed]`
    RustcIfThisChanged(Span, Option<Symbol>),

    /// Represents `#[rustc_inherit_overflow_checks]`
    RustcInheritOverflowChecks,

    /// Represents `#[rustc_insignificant_dtor]`
    RustcInsignificantDtor,

    /// Represents `#[rustc_intrinsic]`
    RustcIntrinsic,

    /// Represents `#[rustc_intrinsic_const_stable_indirect]`
    RustcIntrinsicConstStableIndirect,

    /// Represents `#[rustc_legacy_const_generics]`
    RustcLegacyConstGenerics {
        fn_indexes: ThinVec<(usize, Span)>,
        attr_span: Span,
    },

    /// Represents `#[rustc_lint_opt_deny_field_access]`
    RustcLintOptDenyFieldAccess {
        lint_message: Symbol,
    },

    /// Represents `#[rustc_lint_opt_ty]`
    RustcLintOptTy,

    /// Represents `#[rustc_lint_query_instability]`
    RustcLintQueryInstability,

    /// Represents `#[rustc_lint_untracked_query_information]`
    RustcLintUntrackedQueryInformation,

    /// Represents `#[rustc_macro_transparency]`.
    RustcMacroTransparency(Transparency),

    /// Represents `#[rustc_main]`.
    RustcMain,

    /// Represents `#[rustc_mir]`.
    RustcMir(ThinVec<RustcMirKind>),

    /// Represents `#[rustc_must_implement_one_of]`
    RustcMustImplementOneOf {
        attr_span: Span,
        fn_names: ThinVec<Ident>,
    },

    /// Represents `#[rustc_must_match_exhaustively]`
    RustcMustMatchExhaustively(Span),

    /// Represents `#[rustc_never_returns_null_ptr]`
    RustcNeverReturnsNullPtr,

    /// Represents `#[rustc_never_type_options]`.
    RustcNeverTypeOptions {
        fallback: Option<DivergingFallbackBehavior>,
        diverging_block_default: Option<DivergingBlockBehavior>,
    },

    /// Represents `#[rustc_no_implicit_autorefs]`
    RustcNoImplicitAutorefs,

    /// Represents `#[rustc_no_implicit_bounds]`
    RustcNoImplicitBounds,

    /// Represents `#[rustc_no_mir_inline]`
    RustcNoMirInline,

    /// Represents `#[rustc_no_writable]`
    RustcNoWritable,

    /// Represents `#[rustc_non_const_trait_method]`.
    RustcNonConstTraitMethod,

    /// Represents `#[rustc_nonnull_optimization_guaranteed]`.
    RustcNonnullOptimizationGuaranteed,

    /// Represents `#[rustc_nounwind]`
    RustcNounwind,

    /// Represents `#[rustc_objc_class]`
    RustcObjcClass {
        classname: Symbol,
    },

    /// Represents `#[rustc_objc_selector]`
    RustcObjcSelector {
        methname: Symbol,
    },

    /// Represents `#[rustc_offload_kernel]`
    RustcOffloadKernel,

    /// Represents `#[rustc_paren_sugar]`.
    RustcParenSugar,

    /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
    RustcPassByValue,

    /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
    RustcPassIndirectlyInNonRusticAbis(Span),

    /// Represents `#[rustc_preserve_ub_checks]`
    RustcPreserveUbChecks,

    /// Represents `#[rustc_proc_macro_decls]`
    RustcProcMacroDecls,

    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
    RustcPubTransparent(Span),

    /// Represents `#[rustc_reallocator]`
    RustcReallocator,

    /// Represents `#[rustc_regions]`
    RustcRegions,

    /// Represents `#[rustc_reservation_impl]`
    RustcReservationImpl(Symbol),

    /// Represents `#[rustc_scalable_vector(N)]`
    RustcScalableVector {
        /// The base multiple of lanes that are in a scalable vector, if provided. `element_count`
        /// is not provided for representing tuple types.
        element_count: Option<u16>,
    },

    /// Represents `#[rustc_should_not_be_called_on_const_items]`
    RustcShouldNotBeCalledOnConstItems,

    /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
    RustcSimdMonomorphizeLaneLimit(Limit),

    /// Represents `#[rustc_skip_during_method_dispatch]`.
    RustcSkipDuringMethodDispatch {
        array: bool,
        boxed_slice: bool,
    },

    /// Represents `#[rustc_specialization_trait]`.
    RustcSpecializationTrait,

    /// Represents `#[rustc_std_internal_symbol]`.
    RustcStdInternalSymbol,

    /// Represents `#[rustc_strict_coherence]`.
    RustcStrictCoherence(Span),

    /// Represents `#[rustc_test_marker]`
    RustcTestMarker(Symbol),

    /// Represents `#[rustc_then_this_would_need]`
    RustcThenThisWouldNeed(ThinVec<Ident>),

    /// Represents `#[rustc_trivial_field_reads]`
    RustcTrivialFieldReads,

    /// Represents `#[rustc_unsafe_specialization_marker]`.
    RustcUnsafeSpecializationMarker,

    /// Represents `#[sanitize]`
    ///
    /// the on set and off set are distjoint since there's a third option: unset.
    /// a node may not set the sanitizer setting in which case it inherits from parents.
    /// rtsan is unset if None
    Sanitize {
        on_set: SanitizerSet,
        off_set: SanitizerSet,
        rtsan: Option<RtsanSetting>,
        span: Span,
    },

    /// Represents `#[should_panic]`
    ShouldPanic {
        reason: Option<Symbol>,
    },

    /// Represents `#[splat]`
    Splat(Span),

    /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
    Stability {
        stability: Stability,
        /// Span of the attribute.
        span: Span,
    },

    /// Represents `#[target_feature(enable = "...")]` and
    /// `#[unsafe(force_target_feature(enable = "...")]`.
    TargetFeature {
        features: ThinVec<(Symbol, Span)>,
        attr_span: Span,
        was_forced: bool,
    },

    /// Represents `#![test_runner(path)]`
    TestRunner(Path),

    /// Represents `#[thread_local]`
    ThreadLocal,

    /// Represents `#[track_caller]`
    TrackCaller(Span),

    /// Represents `#[type_length_limit]`
    TypeLengthLimit {
        limit: Limit,
    },

    /// Represents `#[unstable_feature_bound]`.
    UnstableFeatureBound(ThinVec<(Symbol, Span)>),

    /// Represents all `#![unstable_removed(...)]` features
    UnstableRemoved(ThinVec<UnstableRemovedFeature>),

    /// Represents `#[used]`
    Used {
        used_by: UsedBy,
    },

    /// Represents `#[windows_subsystem]`.
    WindowsSubsystem(WindowsSubsystemKind),
    // tidy-alphabetical-end
}
