about summary refs log tree commit diff
path: root/compiler/rustc_hir/src/attrs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir/src/attrs')
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs476
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs87
-rw-r--r--compiler/rustc_hir/src/attrs/mod.rs54
-rw-r--r--compiler/rustc_hir/src/attrs/pretty_printing.rs149
4 files changed, 766 insertions, 0 deletions
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
new file mode 100644
index 00000000000..5f419315467
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -0,0 +1,476 @@
+pub use ReprAttr::*;
+use rustc_abi::Align;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrStyle, ast};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
+use rustc_span::def_id::DefId;
+use rustc_span::hygiene::Transparency;
+use rustc_span::{Ident, Span, Symbol};
+use thin_vec::ThinVec;
+
+use crate::attrs::pretty_printing::PrintAttribute;
+use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
+
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, 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(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
+pub enum InstructionSetAttr {
+    ArmA32,
+    ArmT32,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
+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, HashStable_Generic, 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, HashStable_Generic, PrintAttribute)]
+pub enum IntType {
+    SignedInt(ast::IntTy),
+    UnsignedInt(ast::UintTy),
+}
+
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
+pub struct Deprecation {
+    pub since: DeprecatedSince,
+    /// The note to issue a reason.
+    pub note: Option<Symbol>,
+    /// 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, HashStable_Generic, 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(HashStable_Generic, PrintAttribute)]
+pub enum CoverageAttrKind {
+    On,
+    Off,
+}
+
+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 semantically equivalent to `#[used(linker)]` except that the latter is currently unstable.
+/// `#[used(compiler)]`
+/// `#[used(linker)]`
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum UsedBy {
+    Compiler,
+    Linker,
+}
+
+#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum MacroUseArgs {
+    UseAll,
+    UseSpecific(ThinVec<Ident>),
+}
+
+impl Default for MacroUseArgs {
+    fn default() -> Self {
+        Self::UseSpecific(ThinVec::new())
+    }
+}
+
+#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
+pub struct StrippedCfgItem<ModId = DefId> {
+    pub parent_module: ModId,
+    pub ident: Ident,
+    pub cfg: (CfgEntry, Span),
+}
+
+impl<ModId> StrippedCfgItem<ModId> {
+    pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
+        StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
+    }
+}
+
+#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum CfgEntry {
+    All(ThinVec<CfgEntry>, Span),
+    Any(ThinVec<CfgEntry>, Span),
+    Not(Box<CfgEntry>, Span),
+    Bool(bool, Span),
+    NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span },
+    Version(Option<RustcVersion>, Span),
+}
+
+/// 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, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
+pub enum AttributeKind {
+    // tidy-alphabetical-start
+    /// Represents `#[align(N)]`.
+    // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
+    Align { align: Align, span: Span },
+
+    /// Represents `#[rustc_allow_const_fn_unstable]`.
+    AllowConstFnUnstable(ThinVec<Symbol>, Span),
+
+    /// Represents `#[rustc_allow_incoherent_impl]`.
+    AllowIncoherentImpl(Span),
+
+    /// Represents `#[allow_internal_unstable]`.
+    AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
+
+    /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
+    AsPtr(Span),
+
+    /// Represents `#[automatically_derived]`
+    AutomaticallyDerived(Span),
+
+    /// Represents `#[rustc_default_body_unstable]`.
+    BodyStability {
+        stability: DefaultBodyStability,
+        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
+        span: Span,
+    },
+
+    /// Represents `#[rustc_coherence_is_core]`.
+    CoherenceIsCore,
+
+    /// Represents `#[rustc_coinductive]`.
+    Coinductive(Span),
+
+    /// Represents `#[cold]`.
+    Cold(Span),
+
+    /// Represents `#[rustc_confusables]`.
+    Confusables {
+        symbols: ThinVec<Symbol>,
+        // FIXME(jdonszelmann): remove when target validation code is moved
+        first_span: Span,
+    },
+
+    /// Represents `#[const_continue]`.
+    ConstContinue(Span),
+
+    /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
+    ConstStability {
+        stability: PartialConstStability,
+        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
+        span: Span,
+    },
+
+    /// Represents `#[rustc_const_stable_indirect]`.
+    ConstStabilityIndirect,
+
+    /// Represents `#[const_trait]`.
+    ConstTrait(Span),
+
+    /// Represents `#[coroutine]`.
+    Coroutine(Span),
+
+    /// Represents `#[coverage(..)]`.
+    Coverage(Span, CoverageAttrKind),
+
+    ///Represents `#[rustc_deny_explicit_impl]`.
+    DenyExplicitImpl(Span),
+
+    /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
+    Deprecation { deprecation: Deprecation, span: Span },
+
+    /// Represents `#[rustc_do_not_implement_via_object]`.
+    DoNotImplementViaObject(Span),
+
+    /// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
+    DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
+
+    /// Represents `#[rustc_dummy]`.
+    Dummy,
+
+    /// 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 `#[ffi_const]`.
+    FfiConst(Span),
+
+    /// 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 `#[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, span: Span },
+
+    /// Represents `#[loop_match]`.
+    LoopMatch(Span),
+
+    /// Represents `#[macro_escape]`.
+    MacroEscape(Span),
+
+    /// Represents `#[rustc_macro_transparency]`.
+    MacroTransparency(Transparency),
+
+    /// Represents `#[macro_use]`.
+    MacroUse { span: Span, arguments: MacroUseArgs },
+
+    /// Represents `#[marker]`.
+    Marker(Span),
+
+    /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
+    MayDangle(Span),
+
+    /// Represents `#[must_use]`.
+    MustUse {
+        span: Span,
+        /// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
+        reason: Option<Symbol>,
+    },
+
+    /// Represents `#[naked]`
+    Naked(Span),
+
+    /// Represents `#[no_implicit_prelude]`
+    NoImplicitPrelude(Span),
+
+    /// Represents `#[no_mangle]`
+    NoMangle(Span),
+
+    /// Represents `#[non_exhaustive]`
+    NonExhaustive(Span),
+
+    /// Represents `#[optimize(size|speed)]`
+    Optimize(OptimizeAttr, Span),
+
+    /// Represents `#[rustc_paren_sugar]`.
+    ParenSugar(Span),
+
+    /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
+    PassByValue(Span),
+
+    /// Represents `#[path]`
+    Path(Symbol, Span),
+
+    /// Represents `#[pointee]`
+    Pointee(Span),
+
+    /// Represents `#[proc_macro]`
+    ProcMacro(Span),
+
+    /// Represents `#[proc_macro_attribute]`
+    ProcMacroAttribute(Span),
+
+    /// Represents `#[proc_macro_derive]`
+    ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
+
+    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
+    PubTransparent(Span),
+
+    /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
+    Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
+
+    /// Represents `#[rustc_builtin_macro]`.
+    RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
+
+    /// Represents `#[rustc_layout_scalar_valid_range_end]`.
+    RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
+
+    /// Represents `#[rustc_layout_scalar_valid_range_start]`.
+    RustcLayoutScalarValidRangeStart(Box<u128>, Span),
+
+    /// Represents `#[rustc_object_lifetime_default]`.
+    RustcObjectLifetimeDefault,
+
+    /// Represents `#[should_panic]`
+    ShouldPanic { reason: Option<Symbol>, span: Span },
+
+    /// Represents `#[rustc_skip_during_method_dispatch]`.
+    SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
+
+    /// Represents `#[rustc_specialization_trait]`.
+    SpecializationTrait(Span),
+
+    /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
+    Stability {
+        stability: Stability,
+        /// Span of the attribute.
+        span: Span,
+    },
+
+    /// Represents `#[rustc_std_internal_symbol]`.
+    StdInternalSymbol(Span),
+
+    /// Represents `#[target_feature(enable = "...")]`
+    TargetFeature(ThinVec<(Symbol, Span)>, Span),
+
+    /// Represents `#[track_caller]`
+    TrackCaller(Span),
+
+    /// Represents `#[type_const]`.
+    TypeConst(Span),
+
+    /// Represents `#[rustc_unsafe_specialization_marker]`.
+    UnsafeSpecializationMarker(Span),
+
+    /// Represents `#[unstable_feature_bound]`.
+    UnstableFeatureBound(ThinVec<(Symbol, Span)>),
+
+    /// Represents `#[used]`
+    Used { used_by: UsedBy, span: Span },
+    // tidy-alphabetical-end
+}
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
new file mode 100644
index 00000000000..e3a7f0b97a8
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -0,0 +1,87 @@
+use crate::attrs::AttributeKind;
+
+#[derive(PartialEq)]
+pub enum EncodeCrossCrate {
+    Yes,
+    No,
+}
+
+impl AttributeKind {
+    pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
+        use AttributeKind::*;
+        use EncodeCrossCrate::*;
+
+        match self {
+            // tidy-alphabetical-start
+            Align { .. } => No,
+            AllowConstFnUnstable(..) => No,
+            AllowIncoherentImpl(..) => No,
+            AllowInternalUnstable(..) => Yes,
+            AsPtr(..) => Yes,
+            AutomaticallyDerived(..) => Yes,
+            BodyStability { .. } => No,
+            CoherenceIsCore => No,
+            Coinductive(..) => No,
+            Cold(..) => No,
+            Confusables { .. } => Yes,
+            ConstContinue(..) => No,
+            ConstStability { .. } => Yes,
+            ConstStabilityIndirect => No,
+            ConstTrait(..) => No,
+            Coroutine(..) => No,
+            Coverage(..) => No,
+            DenyExplicitImpl(..) => No,
+            Deprecation { .. } => Yes,
+            DoNotImplementViaObject(..) => No,
+            DocComment { .. } => Yes,
+            Dummy => No,
+            ExportName { .. } => Yes,
+            ExportStable => No,
+            FfiConst(..) => No,
+            FfiPure(..) => No,
+            Fundamental { .. } => Yes,
+            Ignore { .. } => No,
+            Inline(..) => No,
+            LinkName { .. } => Yes, // Needed for rustdoc
+            LinkOrdinal { .. } => No,
+            LinkSection { .. } => Yes, // Needed for rustdoc
+            LoopMatch(..) => No,
+            MacroEscape(..) => No,
+            MacroTransparency(..) => Yes,
+            MacroUse { .. } => No,
+            Marker(..) => No,
+            MayDangle(..) => No,
+            MustUse { .. } => Yes,
+            Naked(..) => No,
+            NoImplicitPrelude(..) => No,
+            NoMangle(..) => Yes,      // Needed for rustdoc
+            NonExhaustive(..) => Yes, // Needed for rustdoc
+            Optimize(..) => No,
+            ParenSugar(..) => No,
+            PassByValue(..) => Yes,
+            Path(..) => No,
+            Pointee(..) => No,
+            ProcMacro(..) => No,
+            ProcMacroAttribute(..) => No,
+            ProcMacroDerive { .. } => No,
+            PubTransparent(..) => Yes,
+            Repr { .. } => No,
+            RustcBuiltinMacro { .. } => Yes,
+            RustcLayoutScalarValidRangeEnd(..) => Yes,
+            RustcLayoutScalarValidRangeStart(..) => Yes,
+            RustcObjectLifetimeDefault => No,
+            ShouldPanic { .. } => No,
+            SkipDuringMethodDispatch { .. } => No,
+            SpecializationTrait(..) => No,
+            Stability { .. } => Yes,
+            StdInternalSymbol(..) => No,
+            TargetFeature(..) => No,
+            TrackCaller(..) => Yes,
+            TypeConst(..) => Yes,
+            UnsafeSpecializationMarker(..) => No,
+            UnstableFeatureBound(..) => No,
+            Used { .. } => No,
+            // tidy-alphabetical-end
+        }
+    }
+}
diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs
new file mode 100644
index 00000000000..482e6c90739
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/mod.rs
@@ -0,0 +1,54 @@
+//! Data structures for representing parsed attributes in the Rust compiler.
+//! Formerly `rustc_attr_data_structures`.
+//!
+//! For detailed documentation about attribute processing,
+//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html).
+
+pub use data_structures::*;
+pub use encode_cross_crate::EncodeCrossCrate;
+pub use pretty_printing::PrintAttribute;
+
+mod data_structures;
+mod encode_cross_crate;
+mod pretty_printing;
+
+/// Finds attributes in sequences of attributes by pattern matching.
+///
+/// A little like `matches` but for attributes.
+///
+/// ```rust,ignore (illustrative)
+/// // finds the repr attribute
+/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
+///
+/// }
+///
+/// // checks if one has matched
+/// if find_attr!(attrs, AttributeKind::Repr(_)) {
+///
+/// }
+/// ```
+///
+/// Often this requires you to first end up with a list of attributes.
+/// A common way to get those is through `tcx.get_all_attrs(did)`
+#[macro_export]
+macro_rules! find_attr {
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
+        $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
+    }};
+
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
+        'done: {
+            for i in $attributes_list {
+                let i: &rustc_hir::Attribute = i;
+                match i {
+                    rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
+                        break 'done Some($e);
+                    }
+                    _ => {}
+                }
+            }
+
+            None
+        }
+    }};
+}
diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs
new file mode 100644
index 00000000000..e44b29141da
--- /dev/null
+++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs
@@ -0,0 +1,149 @@
+use std::num::NonZero;
+
+use rustc_abi::Align;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrStyle, IntTy, UintTy};
+use rustc_ast_pretty::pp::Printer;
+use rustc_span::hygiene::Transparency;
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
+use thin_vec::ThinVec;
+
+/// This trait is used to print attributes in `rustc_hir_pretty`.
+///
+/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
+/// The output will look a lot like a `Debug` implementation, but fields of several types
+/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
+/// representation much.
+pub trait PrintAttribute {
+    /// Whether or not this will render as something meaningful, or if it's skipped
+    /// (which will force the containing struct to also skip printing a comma
+    /// and the field name).
+    fn should_render(&self) -> bool;
+
+    fn print_attribute(&self, p: &mut Printer);
+}
+
+impl PrintAttribute for u128 {
+    fn should_render(&self) -> bool {
+        true
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        p.word(self.to_string())
+    }
+}
+
+impl<T: PrintAttribute> PrintAttribute for &T {
+    fn should_render(&self) -> bool {
+        T::should_render(self)
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        T::print_attribute(self, p)
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for Option<T> {
+    fn should_render(&self) -> bool {
+        self.as_ref().is_some_and(|x| x.should_render())
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        if let Some(i) = self {
+            T::print_attribute(i, p)
+        }
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
+    fn should_render(&self) -> bool {
+        self.is_empty() || self[0].should_render()
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        let mut last_printed = false;
+        p.word("[");
+        for i in self {
+            if last_printed {
+                p.word_space(",");
+            }
+            i.print_attribute(p);
+            last_printed = i.should_render();
+        }
+        p.word("]");
+    }
+}
+macro_rules! print_skip {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { false }
+            fn print_attribute(&self, _: &mut Printer) { }
+        })*
+    };
+}
+
+macro_rules! print_disp {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{}", self));
+            }
+        }
+    )*};
+}
+macro_rules! print_debug {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{:?}", self));
+            }
+        }
+    )*};
+}
+
+macro_rules! print_tup {
+    (num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
+    () => {};
+    ($t: ident $($ts: ident)*) => {
+        #[allow(non_snake_case, unused)]
+        impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
+            fn should_render(&self) -> bool {
+                let ($t, $($ts),*) = self;
+                print_tup!(num_should_render $t $($ts)*) != 0
+            }
+
+            fn print_attribute(&self, p: &mut Printer) {
+                let ($t, $($ts),*) = self;
+                let parens = print_tup!(num_should_render $t $($ts)*) > 1;
+                if parens {
+                    p.popen();
+                }
+
+                let mut printed_anything = $t.should_render();
+
+                $t.print_attribute(p);
+
+                $(
+                    if $ts.should_render() {
+                        if printed_anything {
+                            p.word_space(",");
+                        }
+                        printed_anything = true;
+                    }
+                    $ts.print_attribute(p);
+                )*
+
+                if parens {
+                    p.pclose();
+                }
+            }
+        }
+
+        print_tup!($($ts)*);
+    };
+}
+
+print_tup!(A B C D E F G H);
+print_skip!(Span, (), ErrorGuaranteed);
+print_disp!(u16, bool, NonZero<u32>);
+print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);