about summary refs log tree commit diff
path: root/src/librustdoc
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2025-05-17 14:05:07 +0200
committerLeón Orell Valerian Liehr <me@fmease.dev>2025-09-25 11:49:27 +0200
commit85c193a4ed9cf54a70d6d1edaf411b082d15fd13 (patch)
tree9376eb28698837e7c2954bd8b53cc5a1f8bbd521 /src/librustdoc
parentd7d7725b8cbeea8db490c3b033773776ce8794f5 (diff)
downloadrust-85c193a4ed9cf54a70d6d1edaf411b082d15fd13.tar.gz
rust-85c193a4ed9cf54a70d6d1edaf411b082d15fd13.zip
rustdoc: hide `#[repr(...)]` if it isn't part of the public ABI
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/html/render/mod.rs82
1 files changed, 61 insertions, 21 deletions
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 9280701ea3f..143911cb104 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -2964,6 +2964,10 @@ fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) {
     write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>").unwrap();
 }
 
+/// Compute the *public* `#[repr]` of the item given by `DefId`.
+///
+/// Read more about it here:
+/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
 fn repr_attribute<'tcx>(
     tcx: TyCtxt<'tcx>,
     cache: &Cache,
@@ -2975,25 +2979,59 @@ fn repr_attribute<'tcx>(
     };
     let repr = adt.repr();
 
+    let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
+    let is_public_field = |field: &ty::FieldDef| {
+        (cache.document_private || field.vis.is_public()) && is_visible(field.did)
+    };
+
     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
-            || 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(),
-                );
+        // The transparent repr is public iff the non-1-ZST field is public and visible or
+        // – in case all fields are 1-ZST fields — at least one field is public and visible.
+        let is_public = 'is_public: {
+            // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
+            let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
+
+            if !is_visible(var.def_id) {
+                break 'is_public false;
+            }
+
+            // Side note: There can only ever be one or zero non-1-ZST fields.
+            let non_1zst_field = var.fields.iter().find(|field| {
+                let ty = ty::TypingEnv::post_analysis(tcx, field.did)
+                    .as_query_input(tcx.type_of(field.did).instantiate_identity());
+                tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
+            });
+
+            match non_1zst_field {
+                Some(field) => is_public_field(field),
+                None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
+            }
+        };
 
         // Since the transparent repr can't have any other reprs or
         // repr modifiers beside it, we can safely return early here.
-        return render_transparent.then(|| "#[repr(transparent)]".into());
+        return is_public.then(|| "#[repr(transparent)]".into());
+    }
+
+    // Fast path which avoids looking through the variants and fields in
+    // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
+    // FIXME: This check is not very robust / forward compatible!
+    if !repr.c()
+        && !repr.simd()
+        && repr.int.is_none()
+        && repr.pack.is_none()
+        && repr.align.is_none()
+    {
+        return None;
+    }
+
+    // The repr is public iff all components are public and visible.
+    let is_public = adt
+        .variants()
+        .iter()
+        .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
+    if !is_public {
+        return None;
     }
 
     let mut result = Vec::<Cow<'_, _>>::new();
@@ -3004,12 +3042,6 @@ fn repr_attribute<'tcx>(
     if repr.simd() {
         result.push("simd".into());
     }
-    if let Some(pack) = repr.pack {
-        result.push(format!("packed({})", pack.bytes()).into());
-    }
-    if let Some(align) = repr.align {
-        result.push(format!("align({})", align.bytes()).into());
-    }
     if let Some(int) = repr.int {
         let prefix = if int.is_signed() { 'i' } else { 'u' };
         let int = match int {
@@ -3021,5 +3053,13 @@ fn repr_attribute<'tcx>(
         result.push(int.into());
     }
 
+    // Render modifiers last.
+    if let Some(pack) = repr.pack {
+        result.push(format!("packed({})", pack.bytes()).into());
+    }
+    if let Some(align) = repr.align {
+        result.push(format!("align({})", align.bytes()).into());
+    }
+
     (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
 }