From 8e077219ab666a4e1e3fa49e51831f606ee013c6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 29 Apr 2025 11:30:41 +0200 Subject: [PATCH] Return an iterator instead of a `Vec` in rustdoc `Item::attributes` --- src/librustdoc/clean/types.rs | 118 ++++++++++++++++++----------- src/librustdoc/json/conversions.rs | 2 +- 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index bbe11bf56af30..b2a044c3d8865 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -759,48 +759,9 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { - const ALLOWED_ATTRIBUTES: &[Symbol] = - &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; - + fn adt_item_extra_attrs(&self, tcx: TyCtxt<'_>, document_private: bool) -> Option { use rustc_abi::IntegerType; - let mut attrs: Vec = self - .attrs - .other_attrs - .iter() - .filter_map(|attr| { - if is_json { - match attr { - hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => { - // rustdoc-json stores this in `Item::deprecation`, so we - // don't want it it `Item::attrs`. - None - } - rustc_hir::Attribute::Parsed(rustc_attr_parsing::AttributeKind::Repr( - .., - )) => { - // We have separate pretty-printing logic for `#[repr(..)]` attributes. - // For example, there are circumstances where `#[repr(transparent)]` - // is applied but should not be publicly shown in rustdoc - // because it isn't public API. - None - } - _ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)), - } - } else if attr.has_any_name(ALLOWED_ATTRIBUTES) { - Some( - rustc_hir_pretty::attribute_to_string(&tcx, attr) - .replace("\\\n", "") - .replace('\n', "") - .replace(" ", " "), - ) - } else { - None - } - }) - .collect(); - // Add #[repr(...)] if let Some(def_id) = self.def_id() && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() @@ -814,7 +775,7 @@ impl Item { if repr.transparent() { // Render `repr(transparent)` iff the non-1-ZST field is public or at least one // field is public in case all fields are 1-ZST fields. - let render_transparent = cache.document_private + let render_transparent = document_private || adt .all_fields() .find(|field| { @@ -860,10 +821,55 @@ impl Item { out.push(&int_s); } if !out.is_empty() { - attrs.push(format!("#[repr({})]", out.join(", "))); + return Some(format!("#[repr({})]", out.join(", "))); } } - attrs + None + } + + pub(crate) fn attributes( + &self, + tcx: TyCtxt<'_>, + cache: &Cache, + is_json: bool, + ) -> impl Iterator { + const ALLOWED_ATTRIBUTES: &[Symbol] = + &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; + + self.attrs + .other_attrs + .iter() + .filter_map(move |attr| { + if is_json { + match attr { + hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => { + // rustdoc-json stores this in `Item::deprecation`, so we + // don't want it it `Item::attrs`. + None + } + rustc_hir::Attribute::Parsed(rustc_attr_parsing::AttributeKind::Repr( + .., + )) => { + // We have separate pretty-printing logic for `#[repr(..)]` attributes. + // For example, there are circumstances where `#[repr(transparent)]` + // is applied but should not be publicly shown in rustdoc + // because it isn't public API. + None + } + _ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)), + } + } else if attr.has_any_name(ALLOWED_ATTRIBUTES) { + let attr = rustc_hir_pretty::attribute_to_string(&tcx, attr); + Some(if attr.contains(['\n', '\\']) || attr.contains(" ") { + attr.replace("\\\n", "").replace('\n', "").replace(" ", " ") + } else { + attr + }) + } else { + None + } + }) + .chain(AttrIterator::new(self, cache.document_private, tcx)) } pub fn is_doc_hidden(&self) -> bool { @@ -875,6 +881,32 @@ impl Item { } } +struct AttrIterator<'a, 'tcx> { + item: &'a Item, + document_private: bool, + tcx: TyCtxt<'tcx>, + done: bool, +} + +impl<'a, 'tcx> AttrIterator<'a, 'tcx> { + fn new(item: &'a Item, document_private: bool, tcx: TyCtxt<'tcx>) -> Self { + Self { item, document_private, tcx, done: false } + } +} + +impl<'a, 'tcx> Iterator for AttrIterator<'a, 'tcx> { + type Item = String; + + fn next(&mut self) -> Option { + if self.done { + None + } else { + self.done = true; + self.item.adt_item_extra_attrs(self.tcx, self.document_private) + } + } +} + #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f446c9fbbd8b3..f9cee3ba69d6c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -40,7 +40,7 @@ impl JsonRenderer<'_> { }) .collect(); let docs = item.opt_doc_value(); - let attrs = item.attributes(self.tcx, self.cache(), true); + let attrs = item.attributes(self.tcx, self.cache(), true).collect::>(); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::ItemInner { name, item_id, .. } = *item.inner;