From 5f857a9871240efe6e15ca94dd03e06b90c6413d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 15:40:56 +0200 Subject: [PATCH 1/8] Rename `clean::Enum::variants` method into `non_stripped_variants` --- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render/print_item.rs | 2 +- src/librustdoc/html/render/sidebar.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 07ecd98f77596..f353ad5cf917e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2107,7 +2107,7 @@ impl Enum { self.variants.iter().any(|f| f.is_stripped()) } - pub(crate) fn variants(&self) -> impl Iterator { + pub(crate) fn non_stripped_variants(&self) -> impl Iterator { self.variants.iter().filter(|v| !v.is_stripped()) } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 39a631b637bd4..552e67c9f53d9 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1535,7 +1535,7 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { fmt::from_fn(|w| { - let count_variants = e.variants().count(); + let count_variants = e.non_stripped_variants().count(); wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 361966325fb3c..91540e06e3398 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -599,7 +599,7 @@ fn sidebar_enum<'a>( deref_id_map: &'a DefIdMap, ) { let mut variants = e - .variants() + .non_stripped_variants() .filter_map(|v| v.name) .map(|name| Link::new(format!("variant.{name}"), name.to_string())) .collect::>(); From 560aec13ba62c1a8fd7b2f9fbeada61096809613 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:10:38 +0200 Subject: [PATCH 2/8] Unify rendering of type aliases without ADT items --- src/librustdoc/clean/types.rs | 13 + src/librustdoc/html/render/print_item.rs | 268 +++++++++--------- src/librustdoc/html/templates/item_union.html | 7 +- 3 files changed, 151 insertions(+), 137 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f353ad5cf917e..98d57494dbe68 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -610,6 +610,9 @@ impl Item { UnionItem(ref union_) => Some(union_.has_stripped_entries()), EnumItem(ref enum_) => Some(enum_.has_stripped_entries()), VariantItem(ref v) => v.has_stripped_entries(), + TypeAliasItem(ref type_alias) => { + type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries()) + } _ => None, } } @@ -2345,6 +2348,16 @@ pub(crate) enum TypeAliasInnerType { Struct { ctor_kind: Option, fields: Vec }, } +impl TypeAliasInnerType { + fn has_stripped_entries(&self) -> Option { + Some(match self { + Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), + Self::Union { fields } => fields.iter().any(|f| f.is_stripped()), + Self::Struct { fields, .. } => fields.iter().any(|f| f.is_stripped()), + }) + } +} + #[derive(Clone, Debug)] pub(crate) struct TypeAlias { pub(crate) type_: Type, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 552e67c9f53d9..616d89bca6a73 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1278,94 +1278,40 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); let enum_def_id = ty.ty_adt_def().unwrap().did(); - wrap_item(w, |w| { - let variants_len = variants.len(); - let variants_count = variants_iter().count(); - let has_stripped_entries = variants_len != variants_count; - - write!( - w, - "enum {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_enum_fields( - cx, - Some(&t.generics), - variants, - variants_count, - has_stripped_entries, - *is_non_exhaustive, - enum_def_id, - ) - ) - })?; - write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?; + DisplayEnum { + variants, + generics: &t.generics, + is_non_exhaustive: *is_non_exhaustive, + def_id: enum_def_id, + } + .render_into(cx, it, false, w)?; } clean::TypeAliasInnerType::Union { fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - write!( - w, - "union {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - None, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + ItemUnion { cx, it, fields, generics: &t.generics, document_union: false } + .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - write!( - w, - "struct {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - *ctor_kind, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } + .render_into(cx, it, false, w)?; } } + } else { + let def_id = it.item_id.expect_def_id(); + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + write!( + w, + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + )?; } - let def_id = it.item_id.expect_def_id(); - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!( - w, - "{}{}", - render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) - )?; - // [RUSTDOCIMPL] type.impl // // Include type definitions from the alias target type. @@ -1463,50 +1409,53 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> }) } -fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { - item_template!( - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: &'a Context<'cx>, - it: &'a clean::Item, - s: &'a clean::Union, - }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] - ); - - impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union(&self) -> impl Display { - render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) - } +item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: &'a Context<'cx>, + it: &'a clean::Item, + fields: &'a [clean::Item], + generics: &'a clean::Generics, + document_union: bool, + }, + methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] +); + +impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.generics), &self.fields, self.cx) + } - fn document_field(&self, field: &'a clean::Item) -> impl Display { - document(self.cx, field, Some(self.it), HeadingOffset::H3) - } + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) + } - fn stability_field(&self, field: &clean::Item) -> Option { - field.stability_class(self.cx.tcx()) - } + fn stability_field(&self, field: &clean::Item) -> Option { + field.stability_class(self.cx.tcx()) + } - fn print_ty(&self, ty: &'a clean::Type) -> impl Display { - ty.print(self.cx) - } + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) + } - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.s - .fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() - } + fn fields_iter( + &self, + ) -> iter::Peekable> { + self.fields + .iter() + .filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable() } +} +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, s }.render_into(w).unwrap(); + ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, document_union: true } + .render_into(w) + .unwrap(); Ok(()) }) } @@ -1533,9 +1482,25 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { - fmt::from_fn(|w| { - let count_variants = e.non_stripped_variants().count(); +struct DisplayEnum<'a> { + variants: &'a IndexVec, + generics: &'a clean::Generics, + is_non_exhaustive: bool, + def_id: DefId, +} + +impl<'a> DisplayEnum<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + document_enum: bool, + w: &mut W, + ) -> fmt::Result { + let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let variants_len = self.variants.len(); + let has_stripped_entries = variants_len != variants_count; + wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( @@ -1543,23 +1508,25 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - e.generics.print(cx), + self.generics.print(cx), render_enum_fields( cx, - Some(&e.generics), - &e.variants, - count_variants, - e.has_stripped_entries(), - it.is_non_exhaustive(), - it.def_id().unwrap(), + Some(self.generics), + self.variants, + variants_count, + has_stripped_entries, + self.is_non_exhaustive, + self.def_id, ), ) })?; - write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + if document_enum { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } - if count_variants != 0 { - write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?; + if variants_count != 0 { + write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } let def_id = it.item_id.expect_def_id(); write!( @@ -1568,6 +1535,18 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id) ) + } +} + +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayEnum { + variants: &e.variants, + generics: &e.generics, + is_non_exhaustive: it.is_non_exhaustive(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, true, w) }) } @@ -1955,27 +1934,48 @@ fn item_constant( }) } -fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { - fmt::from_fn(|w| { +struct DisplayStruct<'a> { + ctor_kind: Option, + generics: &'a clean::Generics, + fields: &'a [clean::Item], +} + +impl<'a> DisplayStruct<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + document_struct: bool, + w: &mut W, + ) -> fmt::Result { wrap_item(w, |w| { render_attributes_in_code(w, it, cx); write!( w, "{}", - render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx) + render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx) ) })?; - let def_id = it.item_id.expect_def_id(); + if document_struct { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } + let def_id = it.item_id.expect_def_id(); write!( w, - "{}{}{}{}", - document(cx, it, None, HeadingOffset::H2), - item_fields(cx, it, &s.fields, s.ctor_kind), + "{}{}{}", + item_fields(cx, it, self.fields, self.ctor_kind), render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id), ) + } +} + +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } + .render_into(cx, it, true, w) }) } diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index b1c1d5a63a03c..f3780fb4c5baa 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,15 +2,16 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }} -{{ self.document()|safe }} +{% if self.document_union %} + {{ self.document()|safe }} +{% endif %} {% if self.fields_iter().peek().is_some() %}

{# #} Fields§ {# #}

{% for (field, ty) in self.fields_iter() %} {% let name = field.name.expect("union field name") %} - {# #} + {# #} § {# #} {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} From 4194745687ead479898587b85f1388635b3422e2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:36:32 +0200 Subject: [PATCH 3/8] Split `Item::attributes` method into three --- src/librustdoc/clean/types.rs | 145 ++++++++++++++++------------- src/librustdoc/html/render/mod.rs | 4 +- src/librustdoc/json/conversions.rs | 2 +- 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 98d57494dbe68..1766627ce392c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -764,14 +764,11 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { + pub(crate) fn attributes_witout_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; - use rustc_abi::IntegerType; - - let mut attrs: Vec = self - .attrs + self.attrs .other_attrs .iter() .filter_map(|attr| { @@ -799,74 +796,88 @@ impl Item { None } }) - .collect(); + .collect() + } - // Add #[repr(...)] - if let Some(def_id) = self.def_id() - && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() - { - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - 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 = is_json - || cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = - field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); + pub(crate) fn attributes_and_repr( + &self, + tcx: TyCtxt<'_>, + cache: &Cache, + is_json: bool, + ) -> Vec { + let mut attrs = self.attributes_witout_repr(tcx, is_json); - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { - attrs.push(format!("#[repr({})]", out.join(", "))); - } + if let Some(repr_attr) = self.repr(tcx, cache) { + attrs.push(repr_attr); } attrs } + /// Returns a `#[repr(...)]` representation. + pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { + use rustc_abi::IntegerType; + + let def_id = self.def_id()?; + if !matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + 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 = is_json + || cache.document_private + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of( + ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), + ) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } + } + pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 06cb9269cc87f..6b4fd3cd834a9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1194,7 +1194,7 @@ fn render_assoc_item( // a whitespace prefix and newline. fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display { fmt::from_fn(move |f| { - for a in it.attributes(cx.tcx(), cx.cache(), false) { + for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { writeln!(f, "{prefix}{a}")?; } Ok(()) @@ -1204,7 +1204,7 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { - for attr in it.attributes(cx.tcx(), cx.cache(), false) { + for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { write!(w, "
{attr}
").unwrap(); } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 705f9b2202c60..bfcb794b89a27 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_and_repr(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::ItemInner { name, item_id, .. } = *item.inner; From eb9f05481be266598527ba9b5eb890cc56555733 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 17:44:24 +0200 Subject: [PATCH 4/8] Rename the `document_*` argument/field into `is_type_alias` --- src/librustdoc/html/render/print_item.rs | 22 +++++++++---------- src/librustdoc/html/templates/item_union.html | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 616d89bca6a73..692e11b2689e3 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1287,15 +1287,15 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> is_non_exhaustive: *is_non_exhaustive, def_id: enum_def_id, } - .render_into(cx, it, false, w)?; + .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - ItemUnion { cx, it, fields, generics: &t.generics, document_union: false } + ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true } .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } - .render_into(cx, it, false, w)?; + .render_into(cx, it, true, w)?; } } } else { @@ -1416,7 +1416,7 @@ item_template!( it: &'a clean::Item, fields: &'a [clean::Item], generics: &'a clean::Generics, - document_union: bool, + is_type_alias: bool, }, methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] ); @@ -1453,7 +1453,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, document_union: true } + ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, is_type_alias: false } .render_into(w) .unwrap(); Ok(()) @@ -1494,7 +1494,7 @@ impl<'a> DisplayEnum<'a> { self, cx: &Context<'_>, it: &clean::Item, - document_enum: bool, + is_type_alias: bool, w: &mut W, ) -> fmt::Result { let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); @@ -1521,7 +1521,7 @@ impl<'a> DisplayEnum<'a> { ) })?; - if document_enum { + if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; } @@ -1546,7 +1546,7 @@ fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::D is_non_exhaustive: it.is_non_exhaustive(), def_id: it.def_id().unwrap(), } - .render_into(cx, it, true, w) + .render_into(cx, it, false, w) }) } @@ -1945,7 +1945,7 @@ impl<'a> DisplayStruct<'a> { self, cx: &Context<'_>, it: &clean::Item, - document_struct: bool, + is_type_alias: bool, w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { @@ -1957,7 +1957,7 @@ impl<'a> DisplayStruct<'a> { ) })?; - if document_struct { + if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; } @@ -1975,7 +1975,7 @@ impl<'a> DisplayStruct<'a> { fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { fmt::from_fn(|w| { DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } - .render_into(cx, it, true, w) + .render_into(cx, it, false, w) }) } diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index f3780fb4c5baa..99a9bc874dd59 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,7 +2,7 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }}
-{% if self.document_union %} +{% if !self.is_type_alias %} {{ self.document()|safe }} {% endif %} {% if self.fields_iter().peek().is_some() %} From 4f3dd7b0180b1212aab607202ec6aa0d4fabb323 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 18:47:25 +0200 Subject: [PATCH 5/8] Tweak attribute rendering depending on wether or not it is a type alias --- src/librustdoc/clean/types.rs | 129 ++++++++++++----------- src/librustdoc/html/render/mod.rs | 22 +++- src/librustdoc/html/render/print_item.rs | 100 +++++++++++++++--- tests/rustdoc/type-layout.rs | 2 +- 4 files changed, 173 insertions(+), 80 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1766627ce392c..c0bae5971a42e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -815,67 +815,7 @@ impl Item { /// Returns a `#[repr(...)]` representation. pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { - use rustc_abi::IntegerType; - - let def_id = self.def_id()?; - if !matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) { - return None; - } - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - 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 = is_json - || cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); - - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } + repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json) } pub fn is_doc_hidden(&self) -> bool { @@ -887,6 +827,73 @@ impl Item { } } +pub(crate) fn repr_attributes( + tcx: TyCtxt<'_>, + cache: &Cache, + def_id: DefId, + item_type: ItemType, + is_json: bool, +) -> Option { + use rustc_abi::IntegerType; + + if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + 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 + || is_json + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } +} + #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6b4fd3cd834a9..f155ea52040a1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1201,11 +1201,31 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> }) } +struct CodeAttribute(String); + +impl CodeAttribute { + fn render_into(self, w: &mut impl fmt::Write) { + write!(w, "
{}
", self.0).unwrap(); + } +} + // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { - write!(w, "
{attr}
").unwrap(); + CodeAttribute(attr).render_into(w); + } +} + +/// used for type aliases to only render their `repr` attribute. +fn render_repr_attributes_in_code( + w: &mut impl fmt::Write, + cx: &Context<'_>, + def_id: DefId, + item_type: ItemType, +) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + CodeAttribute(repr).render_into(w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 692e11b2689e3..5f81ec68548ff 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -20,7 +20,7 @@ use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_rightside, render_stability_since_raw, + render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1290,12 +1290,30 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> .render_into(cx, it, true, w)?; } clean::TypeAliasInnerType::Union { fields } => { - ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true } - .render_into(w)?; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let union_def_id = ty.ty_adt_def().unwrap().did(); + + ItemUnion { + cx, + it, + fields, + generics: &t.generics, + is_type_alias: true, + def_id: union_def_id, + } + .render_into(w)?; } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields } - .render_into(cx, it, true, w)?; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let struct_def_id = ty.ty_adt_def().unwrap().did(); + + DisplayStruct { + ctor_kind: *ctor_kind, + generics: &t.generics, + fields, + def_id: struct_def_id, + } + .render_into(cx, it, true, w)?; } } } else { @@ -1417,8 +1435,9 @@ item_template!( fields: &'a [clean::Item], generics: &'a clean::Generics, is_type_alias: bool, + def_id: DefId, }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] + methods = [document, document_type_layout, render_assoc_items] ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { @@ -1449,13 +1468,41 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { }) .peekable() } + + fn render_attributes_in_pre(&self) -> impl fmt::Display { + fmt::from_fn(move |f| { + if !self.is_type_alias { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } + } else { + // For now we only render `repr` attributes for type aliases. + if let Some(repr) = clean::repr_attributes( + self.cx.tcx(), + self.cx.cache(), + self.def_id, + ItemType::Union, + ) { + writeln!(f, "{repr}")?; + }; + } + Ok(()) + }) + } } fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, is_type_alias: false } - .render_into(w) - .unwrap(); + ItemUnion { + cx, + it, + fields: &s.fields, + generics: &s.generics, + is_type_alias: false, + def_id: it.def_id().unwrap(), + } + .render_into(w) + .unwrap(); Ok(()) }) } @@ -1502,7 +1549,12 @@ impl<'a> DisplayEnum<'a> { let has_stripped_entries = variants_len != variants_count; wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } write!( w, "{}enum {}{}{}", @@ -1521,19 +1573,22 @@ impl<'a> DisplayEnum<'a> { ) })?; - if !is_type_alias { + let def_id = it.item_id.expect_def_id(); + let layout_def_id = if !is_type_alias { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - } + def_id + } else { + self.def_id + }; if variants_count != 0 { write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } - let def_id = it.item_id.expect_def_id(); write!( w, "{}{}", render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) + document_type_layout(cx, layout_def_id) ) } } @@ -1938,6 +1993,7 @@ struct DisplayStruct<'a> { ctor_kind: Option, generics: &'a clean::Generics, fields: &'a [clean::Item], + def_id: DefId, } impl<'a> DisplayStruct<'a> { @@ -1949,7 +2005,12 @@ impl<'a> DisplayStruct<'a> { w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } write!( w, "{}", @@ -1974,8 +2035,13 @@ impl<'a> DisplayStruct<'a> { fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { fmt::from_fn(|w| { - DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() } - .render_into(cx, it, false, w) + DisplayStruct { + ctor_kind: s.ctor_kind, + generics: &s.generics, + fields: s.fields.as_slice(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 6de435dbcc143..482b8b597dd30 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -61,7 +61,7 @@ pub type TypeAlias = X; pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); // Regression test for the rustdoc equivalent of #85103. -//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; //@ !hasraw type_layout/trait.MyTrait.html 'Size: ' From 2b292d1b78194e0fb702b1271f4322d0b1d5d1bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 May 2025 18:47:50 +0200 Subject: [PATCH 6/8] Add regression test for #140739 --- tests/rustdoc/type-alias/repr.rs | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/rustdoc/type-alias/repr.rs diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs new file mode 100644 index 0000000000000..cf90798036099 --- /dev/null +++ b/tests/rustdoc/type-alias/repr.rs @@ -0,0 +1,42 @@ +// This test ensures that the `repr` attribute is displayed in type aliases. +// +// Regression test for . + +#![crate_name = "foo"] + +/// bla +#[repr(C)] +pub struct Foo1; + +//@ has 'foo/type.Bar1.html' +//@ has - '//*[@class="rust item-decl"]/code' '#[repr(C)]pub struct Bar1;' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar1 = Foo1; + +/// bla +#[repr(C)] +pub union Foo2 { + pub a: u8, +} + +//@ has 'foo/type.Bar2.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar2 = Foo2; + +/// bla +#[repr(C)] +pub enum Foo3 { + A, +} + +//@ has 'foo/type.Bar3.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]pub enum Bar3 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar3 = Foo3; From 3646a09811bfca55e976117b4c2b8bf0b4cb5aee Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 21 May 2025 22:55:50 +0200 Subject: [PATCH 7/8] Improve code --- src/librustdoc/clean/types.rs | 11 +-- src/librustdoc/html/render/mod.rs | 10 +-- src/librustdoc/html/render/print_item.rs | 76 ++++++++++--------- src/librustdoc/html/templates/item_union.html | 2 +- 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c0bae5971a42e..63533f9fb83a4 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -764,7 +764,7 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes_witout_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { + pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; @@ -805,7 +805,7 @@ impl Item { cache: &Cache, is_json: bool, ) -> Vec { - let mut attrs = self.attributes_witout_repr(tcx, is_json); + let mut attrs = self.attributes_without_repr(tcx, is_json); if let Some(repr_attr) = self.repr(tcx, cache) { attrs.push(repr_attr); @@ -813,7 +813,7 @@ impl Item { attrs } - /// Returns a `#[repr(...)]` representation. + /// Returns a stringified `#[repr(...)]` attribute. pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option { repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json) } @@ -2370,8 +2370,9 @@ impl TypeAliasInnerType { fn has_stripped_entries(&self) -> Option { Some(match self { Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), - Self::Union { fields } => fields.iter().any(|f| f.is_stripped()), - Self::Struct { fields, .. } => fields.iter().any(|f| f.is_stripped()), + Self::Union { fields } | Self::Struct { fields, .. } => { + fields.iter().any(|f| f.is_stripped()) + } }) } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f155ea52040a1..14b35d3c39208 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1203,17 +1203,15 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> struct CodeAttribute(String); -impl CodeAttribute { - fn render_into(self, w: &mut impl fmt::Write) { - write!(w, "
{}
", self.0).unwrap(); - } +fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) { + write!(w, "
{}
", code_attr.0).unwrap(); } // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { - CodeAttribute(attr).render_into(w); + render_code_attribute(CodeAttribute(attr), w); } } @@ -1225,7 +1223,7 @@ fn render_repr_attributes_in_code( item_type: ItemType, ) { if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - CodeAttribute(repr).render_into(w); + render_code_attribute(CodeAttribute(repr), w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5f81ec68548ff..3ef578cb03d0e 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1457,26 +1457,23 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { ty.print(self.cx) } - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() + // FIXME (GuillaumeGomez): When is implemented, + // we can replace the returned value with: + // + // `iter::Peekable>` + // + // And update `item_union.html`. + fn fields_iter(&self) -> impl Iterator { + self.fields.iter().filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) } fn render_attributes_in_pre(&self) -> impl fmt::Display { fmt::from_fn(move |f| { - if !self.is_type_alias { - for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { - writeln!(f, "{a}")?; - } - } else { - // For now we only render `repr` attributes for type aliases. + if self.is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. if let Some(repr) = clean::repr_attributes( self.cx.tcx(), self.cx.cache(), @@ -1485,6 +1482,10 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { ) { writeln!(f, "{repr}")?; }; + } else { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } } Ok(()) }) @@ -1501,8 +1502,7 @@ fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt: is_type_alias: false, def_id: it.def_id().unwrap(), } - .render_into(w) - .unwrap(); + .render_into(w)?; Ok(()) }) } @@ -1529,14 +1529,14 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -struct DisplayEnum<'a> { - variants: &'a IndexVec, - generics: &'a clean::Generics, +struct DisplayEnum<'clean> { + variants: &'clean IndexVec, + generics: &'clean clean::Generics, is_non_exhaustive: bool, def_id: DefId, } -impl<'a> DisplayEnum<'a> { +impl<'clean> DisplayEnum<'clean> { fn render_into( self, cx: &Context<'_>, @@ -1544,16 +1544,16 @@ impl<'a> DisplayEnum<'a> { is_type_alias: bool, w: &mut W, ) -> fmt::Result { - let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); let variants_len = self.variants.len(); - let has_stripped_entries = variants_len != variants_count; + let has_stripped_entries = variants_len != non_stripped_variant_count; wrap_item(w, |w| { - if !is_type_alias { - render_attributes_in_code(w, it, cx); - } else { - // For now we only render `repr` attributes for type aliases. + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } else { + render_attributes_in_code(w, it, cx); } write!( w, @@ -1565,7 +1565,7 @@ impl<'a> DisplayEnum<'a> { cx, Some(self.generics), self.variants, - variants_count, + non_stripped_variant_count, has_stripped_entries, self.is_non_exhaustive, self.def_id, @@ -1574,14 +1574,16 @@ impl<'a> DisplayEnum<'a> { })?; let def_id = it.item_id.expect_def_id(); - let layout_def_id = if !is_type_alias { + let layout_def_id = if is_type_alias { + self.def_id + } else { write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + // We don't return the same `DefId` since the layout size of the type alias might be + // different since we might have more information on the generics. def_id - } else { - self.def_id }; - if variants_count != 0 { + if non_stripped_variant_count != 0 { write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } write!( @@ -2005,11 +2007,11 @@ impl<'a> DisplayStruct<'a> { w: &mut W, ) -> fmt::Result { wrap_item(w, |w| { - if !is_type_alias { - render_attributes_in_code(w, it, cx); - } else { - // For now we only render `repr` attributes for type aliases. + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } else { + render_attributes_in_code(w, it, cx); } write!( w, diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index 99a9bc874dd59..b5d3367a6a10b 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -5,7 +5,7 @@ {% if !self.is_type_alias %} {{ self.document()|safe }} {% endif %} -{% if self.fields_iter().peek().is_some() %} +{% if self.fields_iter().next().is_some() %}

{# #} Fields§ {# #}

From ec97b0f0b57376550efa4d144b62955970f22f02 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 25 May 2025 15:27:32 +0200 Subject: [PATCH 8/8] Update to new API --- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/print_item.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 63533f9fb83a4..bb3469867d51e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -807,7 +807,7 @@ impl Item { ) -> Vec { let mut attrs = self.attributes_without_repr(tcx, is_json); - if let Some(repr_attr) = self.repr(tcx, cache) { + if let Some(repr_attr) = self.repr(tcx, cache, is_json) { attrs.push(repr_attr); } attrs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 14b35d3c39208..5677b13033db5 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1222,7 +1222,7 @@ fn render_repr_attributes_in_code( def_id: DefId, item_type: ItemType, ) { - if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) { render_code_attribute(CodeAttribute(repr), w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3ef578cb03d0e..b4663961c1b71 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1479,6 +1479,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { self.cx.cache(), self.def_id, ItemType::Union, + false, ) { writeln!(f, "{repr}")?; };