//! Built-in attributes and `cfg` flag gating.

use std::sync::LazyLock;

use AttributeGate::*;
use rustc_ast::ast::Safety;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::AttrStyle;
use rustc_span::{Symbol, sym};

use crate::Features;

type GateFn = fn(&Features) -> bool;

pub type GatedCfg = (Symbol, Symbol, GateFn);

/// `cfg(...)`'s that are feature gated.
const GATED_CFGS: &[GatedCfg] = &[
    // (name in cfg, feature, function to check if the feature is enabled)
    (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
    (sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
    (sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks),
    (sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
    (
        sym::target_has_atomic_load_store,
        sym::cfg_target_has_atomic,
        Features::cfg_target_has_atomic,
    ),
    (sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
    (sym::version, sym::cfg_version, Features::cfg_version),
    (sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model),
    (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
    (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
    // this is consistent with naming of the compiler flag it's for
    (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug),
    (sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh),
    (
        sym::target_has_reliable_f16,
        sym::cfg_target_has_reliable_f16_f128,
        Features::cfg_target_has_reliable_f16_f128,
    ),
    (
        sym::target_has_reliable_f16_math,
        sym::cfg_target_has_reliable_f16_f128,
        Features::cfg_target_has_reliable_f16_f128,
    ),
    (
        sym::target_has_reliable_f128,
        sym::cfg_target_has_reliable_f16_f128,
        Features::cfg_target_has_reliable_f16_f128,
    ),
    (
        sym::target_has_reliable_f128_math,
        sym::cfg_target_has_reliable_f16_f128,
        Features::cfg_target_has_reliable_f16_f128,
    ),
    (sym::target_object_format, sym::cfg_target_object_format, Features::cfg_target_object_format),
];

/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
pub fn find_gated_cfg(pred: impl Fn(Symbol) -> bool) -> Option<&'static GatedCfg> {
    GATED_CFGS.iter().find(|(cfg_sym, ..)| pred(*cfg_sym))
}

#[derive(Clone, Debug, Copy)]
pub enum AttributeGate {
    /// A gated attribute which requires a feature gate to be enabled.
    Gated {
        /// The feature gate, for example `#![feature(rustc_attrs)]` for rustc_* attributes.
        feature: Symbol,
        /// The error message displayed when an attempt is made to use the attribute without its feature gate.
        message: &'static str,
        /// Check function to be called during the `PostExpansionVisitor` pass.
        check: fn(&Features) -> bool,
        /// Notes to be displayed when an attempt is made to use the attribute without its feature gate.
        notes: &'static [&'static str],
    },
    /// Ungated attribute, can be used on all release channels
    Ungated,
}

// FIXME(jdonszelmann): move to rustc_hir::attrs
/// A template that the attribute input must match.
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
#[derive(Clone, Copy, Default)]
pub struct AttributeTemplate {
    /// If `true`, the attribute is allowed to be a bare word like `#[test]`.
    pub word: bool,
    /// If `Some`, the attribute is allowed to take a list of items like `#[allow(..)]`.
    pub list: Option<&'static [&'static str]>,
    /// If non-empty, the attribute is allowed to take a list containing exactly
    /// one of the listed words, like `#[coverage(off)]`.
    pub one_of: &'static [Symbol],
    /// If `Some`, the attribute is allowed to be a name/value pair where the
    /// value is a string, like `#[must_use = "reason"]`.
    pub name_value_str: Option<&'static [&'static str]>,
    /// A link to the document for this attribute.
    pub docs: Option<&'static str>,
}

pub enum AttrSuggestionStyle {
    /// The suggestion is styled for a normal attribute.
    /// The `AttrStyle` determines whether this is an inner or outer attribute.
    Attribute(AttrStyle),
    /// The suggestion is styled for an attribute embedded into another attribute.
    /// For example, attributes inside `#[cfg_attr(true, attr(...)]`.
    EmbeddedAttribute,
    /// The suggestion is styled for macros that are parsed with attribute parsers.
    /// For example, the `cfg!(predicate)` macro.
    Macro,
}

impl AttributeTemplate {
    pub fn suggestions(
        &self,
        style: AttrSuggestionStyle,
        safety: Safety,
        name: impl std::fmt::Display,
    ) -> Vec<String> {
        let (start, macro_call, end) = match style {
            AttrSuggestionStyle::Attribute(AttrStyle::Outer) => ("#[", "", "]"),
            AttrSuggestionStyle::Attribute(AttrStyle::Inner) => ("#![", "", "]"),
            AttrSuggestionStyle::Macro => ("", "!", ""),
            AttrSuggestionStyle::EmbeddedAttribute => ("", "", ""),
        };

        let mut suggestions = vec![];

        let (safety_start, safety_end) = match safety {
            Safety::Unsafe(_) => ("unsafe(", ")"),
            _ => ("", ""),
        };

        if self.word {
            debug_assert!(macro_call.is_empty(), "Macro suggestions use list style");
            suggestions.push(format!("{start}{safety_start}{name}{safety_end}{end}"));
        }
        if let Some(descr) = self.list {
            for descr in descr {
                suggestions.push(format!(
                    "{start}{safety_start}{name}{macro_call}({descr}){safety_end}{end}"
                ));
            }
        }
        suggestions.extend(
            self.one_of
                .iter()
                .map(|&word| format!("{start}{safety_start}{name}({word}){safety_end}{end}")),
        );
        if let Some(descr) = self.name_value_str {
            debug_assert!(macro_call.is_empty(), "Macro suggestions use list style");
            for descr in descr {
                suggestions
                    .push(format!("{start}{safety_start}{name} = \"{descr}\"{safety_end}{end}"));
            }
        }
        suggestions.sort();

        suggestions
    }
}

/// A convenience macro for constructing attribute templates.
/// E.g., `template!(Word, List: "description")` means that the attribute
/// supports forms `#[attr]` and `#[attr(description)]`.
#[macro_export]
macro_rules! template {
    (Word) => { $crate::template!(@ true, None, &[], None, None) };
    (Word, $link: literal) => { $crate::template!(@ true, None, &[], None, Some($link)) };
    (List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None, None) };
    (List: $descr: expr, $link: literal) => { $crate::template!(@ false, Some($descr), &[], None, Some($link)) };
    (OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None, None) };
    (NameValueStr: [$($descr: literal),* $(,)?]) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), None) };
    (NameValueStr: [$($descr: literal),* $(,)?], $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), Some($link)) };
    (NameValueStr: $descr: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), None) };
    (NameValueStr: $descr: literal, $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), Some($link)) };
    (Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None, None) };
    (Word, List: $descr: expr, $link: literal) => { $crate::template!(@ true, Some($descr), &[], None, Some($link)) };
    (Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some(&[$descr]), None) };
    (Word, NameValueStr: $descr: expr, $link: literal) => { $crate::template!(@ true, None, &[], Some(&[$descr]), Some($link)) };
    (List: $descr1: expr, NameValueStr: $descr2: expr) => {
        $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), None)
    };
    (List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => {
        $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), Some($link))
    };
    (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
        $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), None)
    };
    (Word, List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => {
        $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), Some($link))
    };
    (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr, $link: expr) => { $crate::AttributeTemplate {
        word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str, docs: $link,
    } };
}

macro_rules! ungated {
    ($attr:ident $(,)?) => {
        BuiltinAttribute { name: sym::$attr, gate: Ungated }
    };
}

macro_rules! gated {
    ($attr:ident, $gate:ident, $message:expr $(,)?) => {
        BuiltinAttribute {
            name: sym::$attr,
            gate: Gated {
                feature: sym::$gate,
                message: $message,
                check: Features::$gate,
                notes: &[],
            },
        }
    };
    ($attr:ident, $message:expr $(,)?) => {
        BuiltinAttribute {
            name: sym::$attr,
            gate: Gated {
                feature: sym::$attr,
                message: $message,
                check: Features::$attr,
                notes: &[],
            },
        }
    };
}

macro_rules! rustc_attr {
    (TEST, $attr:ident $(,)?) => {
        rustc_attr!(
            $attr,
            concat!(
                "the `#[",
                stringify!($attr),
                "]` attribute is used for rustc unit tests"
            ),
        )
    };
    ($attr:ident $(, $notes:expr)* $(,)?) => {
        BuiltinAttribute {
            name: sym::$attr,
            gate: Gated {
                feature: sym::rustc_attrs,
                message: "use of an internal attribute",
                check: Features::rustc_attrs,
                notes: &[
                    concat!("the `#[",
                    stringify!($attr),
                    "]` attribute is an internal implementation detail that will never be stable"),
                    $($notes),*
                ]
            },
        }
    };
}

macro_rules! experimental {
    ($attr:ident) => {
        concat!("the `#[", stringify!($attr), "]` attribute is an experimental feature")
    };
}

pub struct BuiltinAttribute {
    pub name: Symbol,
    pub gate: AttributeGate,
}

/// Attributes that have a special meaning to rustc or rustdoc.
#[rustfmt::skip]
pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
    // ==========================================================================
    // Stable attributes:
    // ==========================================================================

    // Conditional compilation:
    ungated!(cfg),
    ungated!(cfg_attr),

    // Testing:
    ungated!(ignore),
    ungated!(should_panic),

    // Macros:
    ungated!(automatically_derived),
    ungated!(macro_use),
    ungated!(macro_escape), // Deprecated synonym for `macro_use`.
    ungated!(macro_export),
    ungated!(proc_macro),
    ungated!(proc_macro_derive),
    ungated!(proc_macro_attribute),

    // Lints:
    ungated!(warn),
    ungated!(allow),
    ungated!(expect),
    ungated!(forbid),
    ungated!(deny),
    ungated!(must_use),
    gated!(must_not_suspend, experimental!(must_not_suspend)),
    ungated!(deprecated),

    // Crate properties:
    ungated!(crate_name),
    ungated!(crate_type),

    // ABI, linking, symbols, and FFI
    ungated!(link),
    ungated!(link_name),
    ungated!(no_link),
    ungated!(repr),
    // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
    gated!(rustc_align, fn_align, experimental!(rustc_align)),
    gated!(rustc_align_static, static_align, experimental!(rustc_align_static)),
    ungated!(export_name),
    ungated!(link_section),
    ungated!(no_mangle),
    ungated!(used),
    ungated!(link_ordinal),
    ungated!(naked),
    // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
    rustc_attr!(rustc_pass_indirectly_in_non_rustic_abis, "types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic ABIs"),

    // Limits:
    ungated!(recursion_limit),
    ungated!(type_length_limit),
    gated!(move_size_limit, large_assignments, experimental!(move_size_limit)),

    // Entry point:
    ungated!(no_main),

    // Modules, prelude, and resolution:
    ungated!(path),
    ungated!(no_std),
    ungated!(no_implicit_prelude),
    ungated!(non_exhaustive),

    // Runtime
    ungated!(windows_subsystem),
    ungated!(panic_handler), // RFC 2070

    // Code generation:
    ungated!(inline),
    ungated!(cold),
    ungated!(no_builtins),
    ungated!(target_feature),
    ungated!(track_caller),
    ungated!(instruction_set),
    gated!(force_target_feature, effective_target_features, experimental!(force_target_feature)),
    gated!(sanitize, sanitize, experimental!(sanitize)),
    gated!(coverage, coverage_attribute, experimental!(coverage)),

    ungated!(doc),

    // Debugging
    ungated!(debugger_visualizer),
    ungated!(collapse_debuginfo),

    // ==========================================================================
    // Unstable attributes:
    // ==========================================================================

    // Linking:
    gated!(export_stable, experimental!(export_stable)),

    // Testing:
    gated!(test_runner, custom_test_frameworks, "custom test frameworks are an unstable feature"),

    gated!(reexport_test_harness_main, custom_test_frameworks, "custom test frameworks are an unstable feature"),

    // RFC #1268
    gated!(marker, marker_trait_attr, experimental!(marker)),
    gated!(thread_local, "`#[thread_local]` is an experimental feature, and does not currently handle destructors"),
    gated!(no_core, experimental!(no_core)),
    // RFC 2412
    gated!(optimize, optimize_attribute, experimental!(optimize)),

    gated!(ffi_pure, experimental!(ffi_pure)),
    gated!(ffi_const, experimental!(ffi_const)),
    gated!(register_tool, experimental!(register_tool)),
    // `#[cfi_encoding = ""]`
    gated!(cfi_encoding, experimental!(cfi_encoding)),

    // `#[coroutine]` attribute to be applied to closures to make them coroutines instead
    gated!(coroutine, coroutines, experimental!(coroutine)),

    // RFC 3543
    // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
    gated!(patchable_function_entry, experimental!(patchable_function_entry)),

    // The `#[loop_match]` and `#[const_continue]` attributes are part of the
    // lang experiment for RFC 3720 tracked in:
    //
    // - https://github.com/rust-lang/rust/issues/132306
    gated!(const_continue, loop_match, experimental!(const_continue)),
    gated!(loop_match, loop_match, experimental!(loop_match)),

    // The `#[pin_v2]` attribute is part of the `pin_ergonomics` experiment
    // that allows structurally pinning, tracked in:
    //
    // - https://github.com/rust-lang/rust/issues/130494
    gated!(pin_v2, pin_ergonomics, experimental!(pin_v2)),

    // ==========================================================================
    // Internal attributes: Stability, deprecation, and unsafe:
    // ==========================================================================

    ungated!(feature),
    // DuplicatesOk since it has its own validation
    ungated!(stable),
    ungated!(unstable),
    ungated!(unstable_feature_bound),
    ungated!(unstable_removed),
    ungated!(rustc_const_unstable),
    ungated!(rustc_const_stable),
    ungated!(rustc_default_body_unstable),
    gated!(
        allow_internal_unstable,
        "allow_internal_unstable side-steps feature gating and stability checks",
    ),
    gated!(
        allow_internal_unsafe,
        "allow_internal_unsafe side-steps the unsafe_code lint",
    ),
    gated!(
        rustc_eii_foreign_item,
        eii_internals,
        "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
    ),
    rustc_attr!(
        rustc_allowed_through_unstable_modules,
        "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
        through unstable paths"
    ),
    rustc_attr!(
        rustc_deprecated_safe_2024,
        "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary",
    ),
    rustc_attr!(
        rustc_pub_transparent,
        "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
    ),


    // ==========================================================================
    // Internal attributes: Type system related:
    // ==========================================================================

    gated!(fundamental, experimental!(fundamental)),
    gated!(
        may_dangle,
        dropck_eyepatch,
        "`may_dangle` has unstable semantics and may be removed in the future",
    ),

    rustc_attr!(
        rustc_never_type_options,
        "`rustc_never_type_options` is used to experiment with never type fallback and work on \
         never type stabilization"
    ),

    // ==========================================================================
    // Internal attributes: Runtime related:
    // ==========================================================================

    rustc_attr!(rustc_allocator),
    rustc_attr!(rustc_nounwind),
    rustc_attr!(rustc_reallocator),
    rustc_attr!(rustc_deallocator),
    rustc_attr!(rustc_allocator_zeroed),
    rustc_attr!(rustc_allocator_zeroed_variant),
    gated!(
        default_lib_allocator,
        allocator_internals, experimental!(default_lib_allocator),
    ),
    gated!(
        needs_allocator,
        allocator_internals, experimental!(needs_allocator),
    ),
    gated!(
        panic_runtime,
        experimental!(panic_runtime)
    ),
    gated!(
        needs_panic_runtime,
        experimental!(needs_panic_runtime)
    ),
    gated!(
        compiler_builtins,
        "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
        which contains compiler-rt intrinsics and will never be stable",
    ),
    gated!(
        profiler_runtime,
        "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
        which contains the profiler runtime and will never be stable",
    ),

    // ==========================================================================
    // Internal attributes, Linkage:
    // ==========================================================================

    gated!(
        linkage,
        "the `linkage` attribute is experimental and not portable across platforms",
    ),
    rustc_attr!(rustc_std_internal_symbol),
    rustc_attr!(rustc_objc_class),
    rustc_attr!(rustc_objc_selector),

    // ==========================================================================
    // Internal attributes, Macro related:
    // ==========================================================================

    rustc_attr!(rustc_builtin_macro),
    rustc_attr!(rustc_proc_macro_decls),
    rustc_attr!(
        rustc_macro_transparency,
        "used internally for testing macro hygiene",
    ),
    rustc_attr!(rustc_autodiff),
    rustc_attr!(rustc_offload_kernel),
    // Traces that are left when `cfg` and `cfg_attr` attributes are expanded.
    // The attributes are not gated, to avoid stability errors, but they cannot be used in stable
    // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they
    // can only be generated by the compiler.
    ungated!(cfg_trace),
    ungated!(cfg_attr_trace),

    // ==========================================================================
    // Internal attributes, Diagnostics related:
    // ==========================================================================

    rustc_attr!(
        rustc_on_unimplemented,
        "see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute"
    ),
    rustc_attr!(rustc_confusables),
    // Enumerates "identity-like" conversion methods to suggest on type mismatch.
    rustc_attr!(rustc_conversion_suggestion),
    // Prevents field reads in the marked trait or method to be considered
    // during dead code analysis.
    rustc_attr!(rustc_trivial_field_reads),
    // Used by the `rustc::potential_query_instability` lint to warn methods which
    // might not be stable during incremental compilation.
    rustc_attr!(rustc_lint_query_instability),
    // Used by the `rustc::untracked_query_information` lint to warn methods which
    // might not be stable during incremental compilation.
    rustc_attr!(rustc_lint_untracked_query_information),
    // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
    // types (as well as any others in future).
    rustc_attr!(rustc_lint_opt_ty),
    // Used by the `rustc::bad_opt_access` lint on fields
    // types (as well as any others in future).
    rustc_attr!(rustc_lint_opt_deny_field_access),

    // ==========================================================================
    // Internal attributes, Const related:
    // ==========================================================================

    rustc_attr!(rustc_promotable),
    rustc_attr!(rustc_legacy_const_generics),
    // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`.
    rustc_attr!(
        rustc_do_not_const_check,
        "`#[rustc_do_not_const_check]` skips const-check for this function's body",
    ),
    rustc_attr!(
        rustc_const_stable_indirect,
        "this is an internal implementation detail",
    ),
    rustc_attr!(
        rustc_intrinsic_const_stable_indirect,
         "this is an internal implementation detail",
    ),
    rustc_attr!(
        rustc_allow_const_fn_unstable,
        "rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
    ),

    // ==========================================================================
    // Internal attributes, Layout related:
    // ==========================================================================

    rustc_attr!(
        rustc_simd_monomorphize_lane_limit,
        "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \
        for better error messages",
    ),
    rustc_attr!(
        rustc_nonnull_optimization_guaranteed,
        "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \
        guaranteed niche optimizations in the standard library",
        "the compiler does not even check whether the type indeed is being non-null-optimized; \
        it is your responsibility to ensure that the attribute is only used on types that are optimized",
    ),

    // ==========================================================================
    // Internal attributes, Misc:
    // ==========================================================================
    gated!(
        lang, lang_items,
        "lang items are subject to change",
    ),
    rustc_attr!(
        rustc_as_ptr,
        "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations"
    ),
    rustc_attr!(
        rustc_should_not_be_called_on_const_items,
        "`#[rustc_should_not_be_called_on_const_items]` is used to mark methods that don't make sense to be called on interior mutable consts"
    ),
    rustc_attr!(
        rustc_pass_by_value,
        "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference"
    ),
    rustc_attr!(
        rustc_never_returns_null_ptr,
        "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers"
    ),
    rustc_attr!(
        rustc_no_implicit_autorefs,
        "`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument"
    ),
    rustc_attr!(
        rustc_coherence_is_core,
        "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`"
    ),
    rustc_attr!(
        rustc_coinductive,
        "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver"
    ),
    rustc_attr!(
        rustc_allow_incoherent_impl,
        "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl"
    ),
    rustc_attr!(
        rustc_preserve_ub_checks,
        "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR",
    ),
    rustc_attr!(
        rustc_deny_explicit_impl,
        "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls"
    ),
    rustc_attr!(
        rustc_dyn_incompatible_trait,
        "`#[rustc_dyn_incompatible_trait]` marks a trait as dyn-incompatible, \
        even if it otherwise satisfies the requirements to be dyn-compatible."
    ),
    rustc_attr!(
        rustc_has_incoherent_inherent_impls,
        "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \
         the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`"
    ),
    rustc_attr!(
        rustc_non_const_trait_method,
        "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \
        as non-const to allow large traits an easier transition to const"
    ),

    BuiltinAttribute {
        name: sym::rustc_diagnostic_item,
        gate: Gated {
            feature: sym::rustc_attrs,
            message: "use of an internal attribute",
            check: Features::rustc_attrs,
            notes: &["the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types \
            from the standard library for diagnostic purposes"],
        },
    },
    gated!(
        // Used in resolve:
        prelude_import,
        "`#[prelude_import]` is for use by rustc only",
    ),
    gated!(
        rustc_paren_sugar,
        unboxed_closures, "unboxed_closures are still evolving",
    ),
    rustc_attr!(
        rustc_inherit_overflow_checks,
        "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
        overflow checking behavior of several functions in the standard library that are inlined \
        across crates",
    ),
    rustc_attr!(
        rustc_reservation_impl,
        "the `#[rustc_reservation_impl]` attribute is internally used \
        for reserving `impl<T> From<!> for T` as part of the effort to stabilize `!`"
    ),
    rustc_attr!(
        rustc_test_marker,
        "the `#[rustc_test_marker]` attribute is used internally to track tests",
    ),
    rustc_attr!(
        rustc_unsafe_specialization_marker,
        "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations"
    ),
    rustc_attr!(
        rustc_specialization_trait,
        "the `#[rustc_specialization_trait]` attribute is used to check specializations"
    ),
    rustc_attr!(
        rustc_main,
        "the `#[rustc_main]` attribute is used internally to specify test entry point function",
    ),
    rustc_attr!(
        rustc_skip_during_method_dispatch,
        "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
        from method dispatch when the receiver is of the following type, for compatibility in \
        editions < 2021 (array) or editions < 2024 (boxed_slice)"
    ),
    rustc_attr!(
        rustc_must_implement_one_of,
        "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \
        definition of a trait. Its syntax and semantics are highly experimental and will be \
        subject to change before stabilization",
    ),
    rustc_attr!(
        rustc_doc_primitive,
        "the `#[rustc_doc_primitive]` attribute is used by the standard library \
        to provide a way to generate documentation for primitive types",
    ),
    gated!(
        rustc_intrinsic, intrinsics,
        "the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items",
    ),
    rustc_attr!(
        rustc_no_mir_inline,
        "`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen"
    ),
    rustc_attr!(
        rustc_force_inline,
        "`#[rustc_force_inline]` forces a free function to be inlined"
    ),
    rustc_attr!(
        rustc_scalable_vector,
        "`#[rustc_scalable_vector]` defines a scalable vector type"
    ),
    rustc_attr!(
        rustc_must_match_exhaustively,
        "enums with `#[rustc_must_match_exhaustively]` must be matched on with a match block that mentions all variants explicitly"
    ),
    rustc_attr!(
        rustc_no_writable,
        "`#[rustc_no_writable]` stops the compiler from considering mutable reference arguments of this function as implicitly writable"
    ),

    // ==========================================================================
    // Internal attributes, Testing:
    // ==========================================================================

    rustc_attr!(TEST, rustc_effective_visibility),
    rustc_attr!(TEST, rustc_dump_inferred_outlives),
    rustc_attr!(TEST, rustc_capture_analysis,),
    rustc_attr!(TEST, rustc_insignificant_dtor),
    rustc_attr!(TEST, rustc_no_implicit_bounds),
    rustc_attr!(TEST, rustc_strict_coherence),
    rustc_attr!(TEST, rustc_dump_variances),
    rustc_attr!(TEST, rustc_dump_variances_of_opaques),
    rustc_attr!(TEST, rustc_dump_hidden_type_of_opaques),
    rustc_attr!(TEST, rustc_dump_layout),
    rustc_attr!(TEST, rustc_abi),
    rustc_attr!(TEST, rustc_regions),
    rustc_attr!(TEST, rustc_delayed_bug_from_inside_query),
    rustc_attr!(TEST, rustc_dump_user_args),
    rustc_attr!(TEST, rustc_evaluate_where_clauses),
    rustc_attr!(TEST, rustc_if_this_changed),
    rustc_attr!(TEST, rustc_then_this_would_need),
    rustc_attr!(TEST, rustc_clean),
    rustc_attr!(TEST, rustc_partition_reused),
    rustc_attr!(TEST, rustc_partition_codegened),
    rustc_attr!(TEST, rustc_expected_cgu_reuse),
    rustc_attr!(TEST, rustc_dump_symbol_name),
    rustc_attr!(TEST, rustc_dump_def_path),
    rustc_attr!(TEST, rustc_mir),
    gated!(
        custom_mir, "the `#[custom_mir]` attribute is just used for the Rust test suite",
    ),
    rustc_attr!(TEST, rustc_dump_item_bounds),
    rustc_attr!(TEST, rustc_dump_predicates),
    rustc_attr!(TEST, rustc_dump_def_parents),
    rustc_attr!(TEST, rustc_dump_object_lifetime_defaults),
    rustc_attr!(TEST, rustc_dump_vtable),
    rustc_attr!(TEST, rustc_dummy),
    rustc_attr!(TEST, pattern_complexity_limit),
];

pub fn is_builtin_attr_name(name: Symbol) -> bool {
    BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
}

pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> =
    LazyLock::new(|| {
        let mut map = FxHashMap::default();
        for attr in BUILTIN_ATTRIBUTES.iter() {
            if map.insert(attr.name, attr).is_some() {
                panic!("duplicate builtin attribute `{}`", attr.name);
            }
        }
        map
    });
