Skip to content

Commit 4f3dd7b

Browse files
Tweak attribute rendering depending on wether or not it is a type alias
1 parent eb9f054 commit 4f3dd7b

File tree

4 files changed

+173
-80
lines changed

4 files changed

+173
-80
lines changed

src/librustdoc/clean/types.rs

Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -815,67 +815,7 @@ impl Item {
815815

816816
/// Returns a `#[repr(...)]` representation.
817817
pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option<String> {
818-
use rustc_abi::IntegerType;
819-
820-
let def_id = self.def_id()?;
821-
if !matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) {
822-
return None;
823-
}
824-
let adt = tcx.adt_def(def_id);
825-
let repr = adt.repr();
826-
let mut out = Vec::new();
827-
if repr.c() {
828-
out.push("C");
829-
}
830-
if repr.transparent() {
831-
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
832-
// field is public in case all fields are 1-ZST fields.
833-
let render_transparent = is_json
834-
|| cache.document_private
835-
|| adt
836-
.all_fields()
837-
.find(|field| {
838-
let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
839-
tcx.layout_of(
840-
ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty),
841-
)
842-
.is_ok_and(|layout| !layout.is_1zst())
843-
})
844-
.map_or_else(
845-
|| adt.all_fields().any(|field| field.vis.is_public()),
846-
|field| field.vis.is_public(),
847-
);
848-
849-
if render_transparent {
850-
out.push("transparent");
851-
}
852-
}
853-
if repr.simd() {
854-
out.push("simd");
855-
}
856-
let pack_s;
857-
if let Some(pack) = repr.pack {
858-
pack_s = format!("packed({})", pack.bytes());
859-
out.push(&pack_s);
860-
}
861-
let align_s;
862-
if let Some(align) = repr.align {
863-
align_s = format!("align({})", align.bytes());
864-
out.push(&align_s);
865-
}
866-
let int_s;
867-
if let Some(int) = repr.int {
868-
int_s = match int {
869-
IntegerType::Pointer(is_signed) => {
870-
format!("{}size", if is_signed { 'i' } else { 'u' })
871-
}
872-
IntegerType::Fixed(size, is_signed) => {
873-
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
874-
}
875-
};
876-
out.push(&int_s);
877-
}
878-
if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None }
818+
repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json)
879819
}
880820

881821
pub fn is_doc_hidden(&self) -> bool {
@@ -887,6 +827,73 @@ impl Item {
887827
}
888828
}
889829

830+
pub(crate) fn repr_attributes(
831+
tcx: TyCtxt<'_>,
832+
cache: &Cache,
833+
def_id: DefId,
834+
item_type: ItemType,
835+
is_json: bool,
836+
) -> Option<String> {
837+
use rustc_abi::IntegerType;
838+
839+
if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) {
840+
return None;
841+
}
842+
let adt = tcx.adt_def(def_id);
843+
let repr = adt.repr();
844+
let mut out = Vec::new();
845+
if repr.c() {
846+
out.push("C");
847+
}
848+
if repr.transparent() {
849+
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
850+
// field is public in case all fields are 1-ZST fields.
851+
let render_transparent = cache.document_private
852+
|| is_json
853+
|| adt
854+
.all_fields()
855+
.find(|field| {
856+
let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
857+
tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty))
858+
.is_ok_and(|layout| !layout.is_1zst())
859+
})
860+
.map_or_else(
861+
|| adt.all_fields().any(|field| field.vis.is_public()),
862+
|field| field.vis.is_public(),
863+
);
864+
865+
if render_transparent {
866+
out.push("transparent");
867+
}
868+
}
869+
if repr.simd() {
870+
out.push("simd");
871+
}
872+
let pack_s;
873+
if let Some(pack) = repr.pack {
874+
pack_s = format!("packed({})", pack.bytes());
875+
out.push(&pack_s);
876+
}
877+
let align_s;
878+
if let Some(align) = repr.align {
879+
align_s = format!("align({})", align.bytes());
880+
out.push(&align_s);
881+
}
882+
let int_s;
883+
if let Some(int) = repr.int {
884+
int_s = match int {
885+
IntegerType::Pointer(is_signed) => {
886+
format!("{}size", if is_signed { 'i' } else { 'u' })
887+
}
888+
IntegerType::Fixed(size, is_signed) => {
889+
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
890+
}
891+
};
892+
out.push(&int_s);
893+
}
894+
if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None }
895+
}
896+
890897
#[derive(Clone, Debug)]
891898
pub(crate) enum ItemKind {
892899
ExternCrateItem {

src/librustdoc/html/render/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,11 +1201,31 @@ fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) ->
12011201
})
12021202
}
12031203

1204+
struct CodeAttribute(String);
1205+
1206+
impl CodeAttribute {
1207+
fn render_into(self, w: &mut impl fmt::Write) {
1208+
write!(w, "<div class=\"code-attribute\">{}</div>", self.0).unwrap();
1209+
}
1210+
}
1211+
12041212
// When an attribute is rendered inside a <code> tag, it is formatted using
12051213
// a div to produce a newline after it.
12061214
fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
12071215
for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
1208-
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
1216+
CodeAttribute(attr).render_into(w);
1217+
}
1218+
}
1219+
1220+
/// used for type aliases to only render their `repr` attribute.
1221+
fn render_repr_attributes_in_code(
1222+
w: &mut impl fmt::Write,
1223+
cx: &Context<'_>,
1224+
def_id: DefId,
1225+
item_type: ItemType,
1226+
) {
1227+
if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) {
1228+
CodeAttribute(repr).render_into(w);
12091229
}
12101230
}
12111231

src/librustdoc/html/render/print_item.rs

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use super::{
2020
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
2121
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
2222
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
23-
render_impl, render_rightside, render_stability_since_raw,
23+
render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
2424
render_stability_since_raw_with_extra, write_section_heading,
2525
};
2626
use crate::clean;
@@ -1290,12 +1290,30 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
12901290
.render_into(cx, it, true, w)?;
12911291
}
12921292
clean::TypeAliasInnerType::Union { fields } => {
1293-
ItemUnion { cx, it, fields, generics: &t.generics, is_type_alias: true }
1294-
.render_into(w)?;
1293+
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1294+
let union_def_id = ty.ty_adt_def().unwrap().did();
1295+
1296+
ItemUnion {
1297+
cx,
1298+
it,
1299+
fields,
1300+
generics: &t.generics,
1301+
is_type_alias: true,
1302+
def_id: union_def_id,
1303+
}
1304+
.render_into(w)?;
12951305
}
12961306
clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
1297-
DisplayStruct { ctor_kind: *ctor_kind, generics: &t.generics, fields }
1298-
.render_into(cx, it, true, w)?;
1307+
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
1308+
let struct_def_id = ty.ty_adt_def().unwrap().did();
1309+
1310+
DisplayStruct {
1311+
ctor_kind: *ctor_kind,
1312+
generics: &t.generics,
1313+
fields,
1314+
def_id: struct_def_id,
1315+
}
1316+
.render_into(cx, it, true, w)?;
12991317
}
13001318
}
13011319
} else {
@@ -1417,8 +1435,9 @@ item_template!(
14171435
fields: &'a [clean::Item],
14181436
generics: &'a clean::Generics,
14191437
is_type_alias: bool,
1438+
def_id: DefId,
14201439
},
1421-
methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items]
1440+
methods = [document, document_type_layout, render_assoc_items]
14221441
);
14231442

14241443
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
@@ -1449,13 +1468,41 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
14491468
})
14501469
.peekable()
14511470
}
1471+
1472+
fn render_attributes_in_pre(&self) -> impl fmt::Display {
1473+
fmt::from_fn(move |f| {
1474+
if !self.is_type_alias {
1475+
for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) {
1476+
writeln!(f, "{a}")?;
1477+
}
1478+
} else {
1479+
// For now we only render `repr` attributes for type aliases.
1480+
if let Some(repr) = clean::repr_attributes(
1481+
self.cx.tcx(),
1482+
self.cx.cache(),
1483+
self.def_id,
1484+
ItemType::Union,
1485+
) {
1486+
writeln!(f, "{repr}")?;
1487+
};
1488+
}
1489+
Ok(())
1490+
})
1491+
}
14521492
}
14531493

14541494
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
14551495
fmt::from_fn(|w| {
1456-
ItemUnion { cx, it, fields: &s.fields, generics: &s.generics, is_type_alias: false }
1457-
.render_into(w)
1458-
.unwrap();
1496+
ItemUnion {
1497+
cx,
1498+
it,
1499+
fields: &s.fields,
1500+
generics: &s.generics,
1501+
is_type_alias: false,
1502+
def_id: it.def_id().unwrap(),
1503+
}
1504+
.render_into(w)
1505+
.unwrap();
14591506
Ok(())
14601507
})
14611508
}
@@ -1502,7 +1549,12 @@ impl<'a> DisplayEnum<'a> {
15021549
let has_stripped_entries = variants_len != variants_count;
15031550

15041551
wrap_item(w, |w| {
1505-
render_attributes_in_code(w, it, cx);
1552+
if !is_type_alias {
1553+
render_attributes_in_code(w, it, cx);
1554+
} else {
1555+
// For now we only render `repr` attributes for type aliases.
1556+
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum);
1557+
}
15061558
write!(
15071559
w,
15081560
"{}enum {}{}{}",
@@ -1521,19 +1573,22 @@ impl<'a> DisplayEnum<'a> {
15211573
)
15221574
})?;
15231575

1524-
if !is_type_alias {
1576+
let def_id = it.item_id.expect_def_id();
1577+
let layout_def_id = if !is_type_alias {
15251578
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
1526-
}
1579+
def_id
1580+
} else {
1581+
self.def_id
1582+
};
15271583

15281584
if variants_count != 0 {
15291585
write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
15301586
}
1531-
let def_id = it.item_id.expect_def_id();
15321587
write!(
15331588
w,
15341589
"{}{}",
15351590
render_assoc_items(cx, it, def_id, AssocItemRender::All),
1536-
document_type_layout(cx, def_id)
1591+
document_type_layout(cx, layout_def_id)
15371592
)
15381593
}
15391594
}
@@ -1938,6 +1993,7 @@ struct DisplayStruct<'a> {
19381993
ctor_kind: Option<CtorKind>,
19391994
generics: &'a clean::Generics,
19401995
fields: &'a [clean::Item],
1996+
def_id: DefId,
19411997
}
19421998

19431999
impl<'a> DisplayStruct<'a> {
@@ -1949,7 +2005,12 @@ impl<'a> DisplayStruct<'a> {
19492005
w: &mut W,
19502006
) -> fmt::Result {
19512007
wrap_item(w, |w| {
1952-
render_attributes_in_code(w, it, cx);
2008+
if !is_type_alias {
2009+
render_attributes_in_code(w, it, cx);
2010+
} else {
2011+
// For now we only render `repr` attributes for type aliases.
2012+
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct);
2013+
}
19532014
write!(
19542015
w,
19552016
"{}",
@@ -1974,8 +2035,13 @@ impl<'a> DisplayStruct<'a> {
19742035

19752036
fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
19762037
fmt::from_fn(|w| {
1977-
DisplayStruct { ctor_kind: s.ctor_kind, generics: &s.generics, fields: s.fields.as_slice() }
1978-
.render_into(cx, it, false, w)
2038+
DisplayStruct {
2039+
ctor_kind: s.ctor_kind,
2040+
generics: &s.generics,
2041+
fields: s.fields.as_slice(),
2042+
def_id: it.def_id().unwrap(),
2043+
}
2044+
.render_into(cx, it, false, w)
19792045
})
19802046
}
19812047

tests/rustdoc/type-layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub type TypeAlias = X;
6161
pub type GenericTypeAlias = (Generic<(u32, ())>, Generic<u32>);
6262

6363
// Regression test for the rustdoc equivalent of #85103.
64-
//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.'
64+
//@ 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.'
6565
pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>;
6666

6767
//@ !hasraw type_layout/trait.MyTrait.html 'Size: '

0 commit comments

Comments
 (0)