From bd54cd66a1fb36870a2081bbfa6f7362b64679dc Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Mon, 16 Feb 2026 22:02:50 +0000 Subject: [PATCH] Port #[rustc_doc_primitive] to the new attribute parser --- compiler/rustc_ast/src/attr/mod.rs | 7 ++++++ .../src/attributes/rustc_internal.rs | 24 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir/src/hir.rs | 4 ++++ compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_resolve/src/rustdoc.rs | 2 +- src/librustdoc/clean/types.rs | 21 ++++++++-------- src/librustdoc/json/ids.rs | 15 ++++++------ 10 files changed, 59 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index b9411a3269a5..72e7b27a1f97 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -296,6 +296,10 @@ fn is_doc_keyword_or_attribute(&self) -> bool { } false } + + fn is_rustc_doc_primitive(&self) -> bool { + self.has_name(sym::rustc_doc_primitive) + } } impl Attribute { @@ -935,6 +939,9 @@ fn is_proc_macro_attr(&self) -> bool { /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`. fn is_doc_keyword_or_attribute(&self) -> bool; + + /// Returns `true` if this is a `#[rustc_doc_primitive]` attribute. + fn is_rustc_doc_primitive(&self) -> bool; } // FIXME(fn_delegation): use function delegation instead of manually forwarding diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index fa0611bd8358..419d56d8059a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1319,3 +1319,27 @@ impl NoArgsAttributeParser for PreludeImportParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport; } + +pub(crate) struct RustcDocPrimitiveParser; + +impl SingleAttributeParser for RustcDocPrimitiveParser { + const PATH: &[Symbol] = &[sym::rustc_doc_primitive]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None); + return None; + }; + + let Some(value_str) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + Some(AttributeKind::RustcDocPrimitive(cx.attr_span, value_str)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index a0c672778ac1..b4e91ecebeeb 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -205,6 +205,7 @@ mod late { Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b8f3c898ed07..64a94887c2ab 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1169,6 +1169,9 @@ pub enum AttributeKind { /// Represents `#[rustc_do_not_const_check]` RustcDoNotConstCheck, + /// Represents `#[rustc_doc_primitive = ...]` + RustcDocPrimitive(Span, Symbol), + /// Represents `#[rustc_dummy]`. RustcDummy, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 86eafb984121..0b20ea4d6a83 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -117,6 +117,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcDeprecatedSafe2024 { .. } => Yes, RustcDiagnosticItem(..) => Yes, RustcDoNotConstCheck => Yes, + RustcDocPrimitive(..) => Yes, RustcDummy => No, RustcDumpDefParents => No, RustcDumpItemBounds => No, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f1f7350c1e79..960fd206041a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1467,6 +1467,10 @@ fn is_doc_hidden(&self) -> bool { fn is_doc_keyword_or_attribute(&self) -> bool { matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.attribute.is_some() || d.keyword.is_some()) } + + fn is_rustc_doc_primitive(&self) -> bool { + matches!(self, Attribute::Parsed(AttributeKind::RustcDocPrimitive(..))) + } } // FIXME(fn_delegation): use function delegation instead of manually forwarding diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 11c9ee9d61ae..d5051aea0cd4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -312,6 +312,7 @@ fn check_attributes( | AttributeKind::RustcDeprecatedSafe2024 {..} | AttributeKind::RustcDiagnosticItem(..) | AttributeKind::RustcDoNotConstCheck + | AttributeKind::RustcDocPrimitive(..) | AttributeKind::RustcDummy | AttributeKind::RustcDumpDefParents | AttributeKind::RustcDumpItemBounds @@ -402,7 +403,6 @@ fn check_attributes( // internal | sym::rustc_inherit_overflow_checks | sym::rustc_on_unimplemented - | sym::rustc_doc_primitive | sym::rustc_layout | sym::rustc_autodiff | sym::rustc_capture_analysis diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 9f74a7801d2e..89b561bd2e90 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -367,7 +367,7 @@ pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]` or `#[doc(attribute)]`. pub fn has_primitive_or_keyword_or_attribute_docs(attrs: &[impl AttributeExt]) -> bool { for attr in attrs { - if attr.has_name(sym::rustc_doc_primitive) || attr.is_doc_keyword_or_attribute() { + if attr.is_rustc_doc_primitive() || attr.is_doc_keyword_or_attribute() { return true; } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index bd6399adc3b8..bdd3a691e95f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -308,17 +308,16 @@ pub(crate) fn primitives( // duplicately for the same primitive. This is handled later on when // rendering by delegating everything to a hash map. fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)> { - tcx.get_attrs(def_id, sym::rustc_doc_primitive).next().map(|attr| { - let attr_value = attr.value_str().expect("syntax should already be validated"); - let Some(prim) = PrimitiveType::from_symbol(attr_value) else { - span_bug!( - attr.span(), - "primitive `{attr_value}` is not a member of `PrimitiveType`" - ); - }; - - (def_id, prim) - }) + let Some((attr_span, prim_sym)) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::RustcDocPrimitive(span, prim) => (*span, *prim) + ) else { + return None; + }; + let Some(prim) = PrimitiveType::from_symbol(prim_sym) else { + span_bug!(attr_span, "primitive `{prim_sym}` is not a member of `PrimitiveType`"); + }; + Some((def_id, prim)) } self.mapped_root_modules(tcx, as_primitive) diff --git a/src/librustdoc/json/ids.rs b/src/librustdoc/json/ids.rs index 737148bad4e8..a3d86b25dada 100644 --- a/src/librustdoc/json/ids.rs +++ b/src/librustdoc/json/ids.rs @@ -6,9 +6,10 @@ //! other phases think of as an "item". use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::DefKind; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; -use rustc_span::{Symbol, sym}; +use rustc_hir::find_attr; +use rustc_span::Symbol; use rustdoc_json_types as types; use super::JsonRenderer; @@ -88,12 +89,10 @@ fn id_from_item_inner( // We need this workaround because primitive types' DefId actually refers to // their parent module, which isn't present in the output JSON items. So // instead, we directly get the primitive symbol - if matches!(self.tcx.def_kind(def_id), DefKind::Mod) - && let Some(prim) = self - .tcx - .get_attrs(def_id, sym::rustc_doc_primitive) - .find_map(|attr| attr.value_str()) - { + if let Some(prim) = find_attr!( + self.tcx.get_all_attrs(def_id), + AttributeKind::RustcDocPrimitive(_, prim) => *prim + ) { Some(prim) } else { self.tcx.opt_item_name(def_id)