about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-05-09 18:47:25 +0200
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2025-05-25 15:10:23 +0200
commit4f3dd7b0180b1212aab607202ec6aa0d4fabb323 (patch)
tree0dd450077b4f8cf956feb90c3e90f221fd22dd7e
parenteb9f05481be266598527ba9b5eb890cc56555733 (diff)
downloadrust-4f3dd7b0180b1212aab607202ec6aa0d4fabb323.tar.gz
rust-4f3dd7b0180b1212aab607202ec6aa0d4fabb323.zip
Tweak attribute rendering depending on wether or not it is a type alias
-rw-r--r--src/librustdoc/clean/types.rs129
-rw-r--r--src/librustdoc/html/render/mod.rs22
-rw-r--r--src/librustdoc/html/render/print_item.rs100
-rw-r--r--tests/rustdoc/type-layout.rs2
4 files changed, 173 insertions, 80 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 1766627ce39..c0bae5971a4 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<String> {
-        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<String> {
+    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 6b4fd3cd834..f155ea52040 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, "<div class=\"code-attribute\">{}</div>", self.0).unwrap();
+    }
+}
+
 // When an attribute is rendered inside a <code> 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, "<div class=\"code-attribute\">{attr}</div>").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 692e11b2689..5f81ec68548 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<CtorKind>,
     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 6de435dbcc1..482b8b597dd 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<u32>);
 
 // 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: '