From c325fda0bfb32affaeba76c360779585278b8c61 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 3 Apr 2023 21:20:45 -0700 Subject: [PATCH] rustdoc: migrate `item_union` to an Askama template --- src/librustdoc/html/format.rs | 4 - src/librustdoc/html/render/mod.rs | 77 +-- src/librustdoc/html/render/print_item.rs | 569 ++++++++++-------- src/librustdoc/html/templates/item_union.html | 23 + 4 files changed, 382 insertions(+), 291 deletions(-) create mode 100644 src/librustdoc/html/templates/item_union.html diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 0895bb510d4..77c2cd5b425 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -136,10 +136,6 @@ impl Buffer { self.into_inner() } - pub(crate) fn is_for_html(&self) -> bool { - self.for_html - } - pub(crate) fn reserve(&mut self, additional: usize) { self.buffer.reserve(additional) } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 49986433dfb..1e3cd266850 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -660,7 +660,7 @@ fn short_item_info( // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). pub(crate) fn render_impls( cx: &mut Context<'_>, - w: &mut Buffer, + mut w: impl Write, impls: &[&Impl], containing_item: &clean::Item, toggle_open_by_default: bool, @@ -672,7 +672,7 @@ pub(crate) fn render_impls( let did = i.trait_did().unwrap(); let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx); let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods); - let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() }; + let mut buffer = Buffer::new(); render_impl( &mut buffer, cx, @@ -693,7 +693,7 @@ pub(crate) fn render_impls( }) .collect::>(); rendered_impls.sort(); - w.write_str(&rendered_impls.join("")); + w.write_str(&rendered_impls.join("")).unwrap(); } /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. @@ -1080,61 +1080,68 @@ impl<'a> AssocItemLink<'a> { } } -fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) { +fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) { write!( w, "

\ {title}\ §\

" - ); + ) + .unwrap(); } pub(crate) fn render_all_impls( - w: &mut Buffer, + mut w: impl Write, cx: &mut Context<'_>, containing_item: &clean::Item, concrete: &[&Impl], synthetic: &[&Impl], blanket_impl: &[&Impl], ) { - let mut impls = Buffer::empty_from(w); + let mut impls = Buffer::html(); render_impls(cx, &mut impls, concrete, containing_item, true); let impls = impls.into_inner(); if !impls.is_empty() { - write_impl_section_heading(w, "Trait Implementations", "trait-implementations"); - write!(w, "
{}
", impls); + write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations"); + write!(w, "
{}
", impls).unwrap(); } if !synthetic.is_empty() { - write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations"); - w.write_str("
"); - render_impls(cx, w, synthetic, containing_item, false); - w.write_str("
"); + write_impl_section_heading( + &mut w, + "Auto Trait Implementations", + "synthetic-implementations", + ); + w.write_str("
").unwrap(); + render_impls(cx, &mut w, synthetic, containing_item, false); + w.write_str("
").unwrap(); } if !blanket_impl.is_empty() { - write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations"); - w.write_str("
"); - render_impls(cx, w, blanket_impl, containing_item, false); - w.write_str("
"); + write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations"); + w.write_str("
").unwrap(); + render_impls(cx, &mut w, blanket_impl, containing_item, false); + w.write_str("
").unwrap(); } } -fn render_assoc_items( - w: &mut Buffer, - cx: &mut Context<'_>, - containing_item: &clean::Item, +fn render_assoc_items<'a, 'cx: 'a>( + cx: &'a mut Context<'cx>, + containing_item: &'a clean::Item, it: DefId, - what: AssocItemRender<'_>, -) { + what: AssocItemRender<'a>, +) -> impl fmt::Display + 'a + Captures<'cx> { let mut derefs = DefIdSet::default(); derefs.insert(it); - render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs) + display_fn(move |f| { + render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs); + Ok(()) + }) } fn render_assoc_items_inner( - w: &mut Buffer, + mut w: &mut dyn fmt::Write, cx: &mut Context<'_>, containing_item: &clean::Item, it: DefId, @@ -1147,7 +1154,7 @@ fn render_assoc_items_inner( let Some(v) = cache.impls.get(&it) else { return }; let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { - let mut tmp_buf = Buffer::empty_from(w); + let mut tmp_buf = Buffer::html(); let (render_mode, id) = match what { AssocItemRender::All => { write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations"); @@ -1171,7 +1178,7 @@ fn render_assoc_items_inner( (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id)) } }; - let mut impls_buf = Buffer::empty_from(w); + let mut impls_buf = Buffer::html(); for i in &non_trait { render_impl( &mut impls_buf, @@ -1191,10 +1198,10 @@ fn render_assoc_items_inner( ); } if !impls_buf.is_empty() { - w.push_buffer(tmp_buf); - write!(w, "
", id); - w.push_buffer(impls_buf); - w.write_str("
"); + write!(w, "{}", tmp_buf.into_inner()).unwrap(); + write!(w, "
", id).unwrap(); + write!(w, "{}", impls_buf.into_inner()).unwrap(); + w.write_str("
").unwrap(); } } @@ -1204,7 +1211,7 @@ fn render_assoc_items_inner( if let Some(impl_) = deref_impl { let has_deref_mut = traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); - render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs); + render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs); } // If we were already one level into rendering deref methods, we don't want to render @@ -1223,7 +1230,7 @@ fn render_assoc_items_inner( } fn render_deref_methods( - w: &mut Buffer, + mut w: impl Write, cx: &mut Context<'_>, impl_: &Impl, container_item: &clean::Item, @@ -1255,10 +1262,10 @@ fn render_deref_methods( return; } } - render_assoc_items_inner(w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); } else if let Some(prim) = target.primitive_type() { if let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items_inner(w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); } } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index ea53f8d107d..6bce5734004 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -202,7 +202,7 @@ fn should_hide_fields(n_fields: usize) -> bool { n_fields > 12 } -fn toggle_open(w: &mut Buffer, text: impl fmt::Display) { +fn toggle_open(mut w: impl fmt::Write, text: impl fmt::Display) { write!( w, "
\ @@ -210,11 +210,12 @@ fn toggle_open(w: &mut Buffer, text: impl fmt::Display) { Show {}\ ", text - ); + ) + .unwrap(); } -fn toggle_close(w: &mut Buffer) { - w.write_str("
"); +fn toggle_close(mut w: impl fmt::Write) { + w.write_str("").unwrap(); } fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { @@ -580,7 +581,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); // Output the trait definition - wrap_item(w, |w| { + wrap_item(w, |mut w| { write!( w, "{attrs}{}{}{}trait {}{}{}", @@ -610,7 +611,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: if should_hide_fields(count_types) { toggle = true; toggle_open( - w, + &mut w, format_args!("{} associated items", count_types + count_consts + count_methods), ); } @@ -634,7 +635,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: if !toggle && should_hide_fields(count_types + count_consts) { toggle = true; toggle_open( - w, + &mut w, format_args!( "{} associated constant{} and {} method{}", count_consts, @@ -662,7 +663,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } if !toggle && should_hide_fields(count_methods) { toggle = true; - toggle_open(w, format_args!("{} methods", count_methods)); + toggle_open(&mut w, format_args!("{} methods", count_methods)); } if count_consts != 0 && count_methods != 0 { w.write_str("\n"); @@ -710,7 +711,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } } if toggle { - toggle_close(w); + toggle_close(&mut w); } w.write_str("}"); } @@ -847,7 +848,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } // If there are methods directly on this trait object, render them here. - render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All); + write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)); let cloned_shared = Rc::clone(&cx.shared); let cache = &cloned_shared.cache; @@ -1074,7 +1075,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & // 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. - render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) + write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) } fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { @@ -1096,7 +1097,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl // 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. - render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) + write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) } fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { @@ -1124,54 +1125,102 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea // 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. - render_assoc_items(w, cx, it, def_id, AssocItemRender::All); - document_type_layout(w, cx, def_id); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", document_type_layout(cx, def_id)); } fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { - wrap_item(w, |w| { - write!(w, "{}", render_attributes_in_pre(it, "")); - render_union(w, it, Some(&s.generics), &s.fields, cx); - }); + #[derive(Template)] + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: std::cell::RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + s: &'a clean::Union, + } - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { + fn render_assoc_items<'b>( + &'b self, + ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let def_id = self.it.item_id.expect_def_id(); + let mut cx = self.cx.borrow_mut(); + let v = render_assoc_items(*cx, self.it, def_id, AssocItemRender::All); + write!(f, "{v}") + }) + } + fn document_type_layout<'b>( + &'b self, + ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let def_id = self.it.item_id.expect_def_id(); + let cx = self.cx.borrow_mut(); + let v = document_type_layout(*cx, def_id); + write!(f, "{v}") + }) + } + fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let cx = self.cx.borrow_mut(); + let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, *cx); + write!(f, "{v}") + }) + } + fn render_attributes_in_pre<'b>( + &'b self, + ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let v = render_attributes_in_pre(self.it, ""); + write!(f, "{v}") + }) + } + fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let mut cx = self.cx.borrow_mut(); + let v = document(*cx, self.it, None, HeadingOffset::H2); + write!(f, "{v}") + }) + } + fn document_field<'b>( + &'b self, + field: &'a clean::Item, + ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let mut cx = self.cx.borrow_mut(); + let v = document(*cx, field, Some(self.it), HeadingOffset::H3); + write!(f, "{v}") + }) + } + fn stability_field(&self, field: &clean::Item) -> Option { + let cx = self.cx.borrow(); + field.stability_class(cx.tcx()) + } + fn print_ty<'b>( + &'b self, + ty: &'a clean::Type, + ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let cx = self.cx.borrow(); + let v = ty.print(*cx); + write!(f, "{v}") + }) + } - let mut fields = s - .fields - .iter() - .filter_map(|f| match *f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if fields.peek().is_some() { - write!( - w, - "

\ - Fields§\ -

" - ); - for (field, ty) in fields { - let name = field.name.expect("union field name"); - let id = format!("{}.{}", ItemType::StructField, name); - write!( - w, - "\ - §\ - {name}: {ty}\ - ", - shortty = ItemType::StructField, - ty = ty.print(cx), - ); - if let Some(stability_class) = field.stability_class(cx.tcx()) { - write!(w, ""); - } - write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); + fn fields_iter( + &self, + ) -> std::iter::Peekable> { + self.s + .fields + .iter() + .filter_map(|f| match *f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable() } } - let def_id = it.item_id.expect_def_id(); - render_assoc_items(w, cx, it, def_id, AssocItemRender::All); - document_type_layout(w, cx, def_id); + + ItemUnion { cx: std::cell::RefCell::new(cx), it, s }.render_into(w).unwrap(); } fn print_tuple_struct_fields<'a, 'cx: 'a>( @@ -1196,7 +1245,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { let tcx = cx.tcx(); let count_variants = e.variants().count(); - wrap_item(w, |w| { + wrap_item(w, |mut w| { write!( w, "{attrs}{}enum {}{}", @@ -1217,7 +1266,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str("{\n"); let toggle = should_hide_fields(count_variants); if toggle { - toggle_open(w, format_args!("{} variants", count_variants)); + toggle_open(&mut w, format_args!("{} variants", count_variants)); } for v in e.variants() { w.write_str(" "); @@ -1242,7 +1291,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str(" // some variants omitted\n"); } if toggle { - toggle_close(w); + toggle_close(&mut w); } w.write_str("}"); } @@ -1255,11 +1304,12 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w, "

\ Variants{}§\ -

", - document_non_exhaustive_header(it) + \ + {}\ +
", + document_non_exhaustive_header(it), + document_non_exhaustive(it) ); - document_non_exhaustive(w, it); - write!(w, "
"); for variant in e.variants() { let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); write!( @@ -1304,9 +1354,10 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: write!( w, "
\ -

{heading}

", +

{heading}

\ + {}", + document_non_exhaustive(variant) ); - document_non_exhaustive(w, variant); for field in fields { match *field.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => {} @@ -1343,8 +1394,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: write!(w, "
"); } let def_id = it.item_id.expect_def_id(); - render_assoc_items(w, cx, it, def_id, AssocItemRender::All); - document_type_layout(w, cx, def_id); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", document_type_layout(cx, def_id)); } fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { @@ -1382,7 +1433,7 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - render_assoc_items(w, cx, it, def_id, AssocItemRender::All); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. @@ -1463,11 +1514,12 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean w, "

\ {}{}§\ -

", + \ + {}", if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, - document_non_exhaustive_header(it) + document_non_exhaustive_header(it), + document_non_exhaustive(it) ); - document_non_exhaustive(w, it); for (index, (field, ty)) in fields.enumerate() { let field_name = field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string()); @@ -1486,8 +1538,8 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } } let def_id = it.item_id.expect_def_id(); - render_assoc_items(w, cx, it, def_id, AssocItemRender::All); - document_type_layout(w, cx, def_id); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", document_type_layout(cx, def_id)); } fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { @@ -1519,7 +1571,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) + write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1660,64 +1712,69 @@ fn render_implementor( ); } -fn render_union( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - fields: &[clean::Item], - cx: &Context<'_>, -) { - let tcx = cx.tcx(); - write!( - w, - "{}union {}", - visibility_print_with_space(it.visibility(tcx), it.item_id, cx), - it.name.unwrap(), - ); +fn render_union<'a, 'cx: 'a>( + it: &'a clean::Item, + g: Option<&'a clean::Generics>, + fields: &'a [clean::Item], + cx: &'a Context<'cx>, +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |mut f| { + let tcx = cx.tcx(); + write!( + f, + "{}union {}", + visibility_print_with_space(it.visibility(tcx), it.item_id, cx), + it.name.unwrap(), + )?; - let where_displayed = g - .map(|g| { - write!(w, "{}", g.print(cx)); - print_where_clause_and_check(w, g, cx) - }) - .unwrap_or(false); + let where_displayed = g + .map(|g| { + let mut buf = Buffer::html(); + write!(buf, "{}", g.print(cx)); + let where_displayed = print_where_clause_and_check(&mut buf, g, cx); + write!(f, "{buf}", buf = buf.into_inner()).unwrap(); + where_displayed + }) + .unwrap_or(false); - // If there wasn't a `where` clause, we add a whitespace. - if !where_displayed { - w.write_str(" "); - } - - write!(w, "{{\n"); - let count_fields = - fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); - let toggle = should_hide_fields(count_fields); - if toggle { - toggle_open(w, format_args!("{} fields", count_fields)); - } - - for field in fields { - if let clean::StructFieldItem(ref ty) = *field.kind { - write!( - w, - " {}{}: {},\n", - visibility_print_with_space(field.visibility(tcx), field.item_id, cx), - field.name.unwrap(), - ty.print(cx) - ); + // If there wasn't a `where` clause, we add a whitespace. + if !where_displayed { + f.write_str(" ")?; } - } - if it.has_stripped_entries().unwrap() { - write!(w, " /* private fields */\n"); - } - if toggle { - toggle_close(w); - } - w.write_str("}"); + write!(f, "{{\n")?; + let count_fields = + fields.iter().filter(|field| matches!(*field.kind, clean::StructFieldItem(..))).count(); + let toggle = should_hide_fields(count_fields); + if toggle { + toggle_open(&mut f, format_args!("{} fields", count_fields)); + } + + for field in fields { + if let clean::StructFieldItem(ref ty) = *field.kind { + write!( + f, + " {}{}: {},\n", + visibility_print_with_space(field.visibility(tcx), field.item_id, cx), + field.name.unwrap(), + ty.print(cx) + )?; + } + } + + if it.has_stripped_entries().unwrap() { + write!(f, " /* private fields */\n")?; + } + if toggle { + toggle_close(&mut f); + } + f.write_str("}").unwrap(); + Ok(()) + }) } fn render_struct( - w: &mut Buffer, + mut w: &mut Buffer, it: &clean::Item, g: Option<&clean::Generics>, ty: Option, @@ -1752,7 +1809,7 @@ fn render_struct( let has_visible_fields = count_fields > 0; let toggle = should_hide_fields(count_fields); if toggle { - toggle_open(w, format_args!("{} fields", count_fields)); + toggle_open(&mut w, format_args!("{} fields", count_fields)); } for field in fields { if let clean::StructFieldItem(ref ty) = *field.kind { @@ -1776,7 +1833,7 @@ fn render_struct( write!(w, " /* private fields */ "); } if toggle { - toggle_close(w); + toggle_close(&mut w); } w.write_str("}"); } @@ -1822,161 +1879,169 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str { if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } } -fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { - if item.is_non_exhaustive() { - write!( - w, - "
\ - {}\ -
", - { - if item.is_struct() { - "This struct is marked as non-exhaustive" - } else if item.is_enum() { - "This enum is marked as non-exhaustive" - } else if item.is_variant() { - "This variant is marked as non-exhaustive" - } else { - "This type is marked as non-exhaustive" +fn document_non_exhaustive<'a>(item: &'a clean::Item) -> impl fmt::Display + 'a { + display_fn(|f| { + if item.is_non_exhaustive() { + write!( + f, + "
\ + {}\ +
", + { + if item.is_struct() { + "This struct is marked as non-exhaustive" + } else if item.is_enum() { + "This enum is marked as non-exhaustive" + } else if item.is_variant() { + "This variant is marked as non-exhaustive" + } else { + "This type is marked as non-exhaustive" + } } + )?; + + if item.is_struct() { + f.write_str( + "Non-exhaustive structs could have additional fields added in future. \ + Therefore, non-exhaustive structs cannot be constructed in external crates \ + using the traditional Struct { .. } syntax; cannot be \ + matched against without a wildcard ..; and \ + struct update syntax will not work.", + )?; + } else if item.is_enum() { + f.write_str( + "Non-exhaustive enums could have additional variants added in future. \ + Therefore, when matching against variants of non-exhaustive enums, an \ + extra wildcard arm must be added to account for any future variants.", + )?; + } else if item.is_variant() { + f.write_str( + "Non-exhaustive enum variants could have additional fields added in future. \ + Therefore, non-exhaustive enum variants cannot be constructed in external \ + crates and cannot be matched against.", + )?; + } else { + f.write_str( + "This type will require a wildcard arm in any match statements or constructors.", + )?; } - ); - if item.is_struct() { - w.write_str( - "Non-exhaustive structs could have additional fields added in future. \ - Therefore, non-exhaustive structs cannot be constructed in external crates \ - using the traditional Struct { .. } syntax; cannot be \ - matched against without a wildcard ..; and \ - struct update syntax will not work.", - ); - } else if item.is_enum() { - w.write_str( - "Non-exhaustive enums could have additional variants added in future. \ - Therefore, when matching against variants of non-exhaustive enums, an \ - extra wildcard arm must be added to account for any future variants.", - ); - } else if item.is_variant() { - w.write_str( - "Non-exhaustive enum variants could have additional fields added in future. \ - Therefore, non-exhaustive enum variants cannot be constructed in external \ - crates and cannot be matched against.", - ); - } else { - w.write_str( - "This type will require a wildcard arm in any match statements or constructors.", - ); + f.write_str("
")?; } - - w.write_str("
"); - } + Ok(()) + }) } -fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { - fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) { +fn document_type_layout<'a, 'cx: 'a>( + cx: &'a Context<'cx>, + ty_def_id: DefId, +) -> impl fmt::Display + 'a + Captures<'cx> { + fn write_size_of_layout(mut w: impl fmt::Write, layout: &LayoutS, tag_size: u64) { if layout.abi.is_unsized() { - write!(w, "(unsized)"); + write!(w, "(unsized)").unwrap(); } else { let size = layout.size.bytes() - tag_size; - write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },); + write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" }).unwrap(); if layout.abi.is_uninhabited() { write!( w, " (uninhabited)" - ); + ).unwrap(); } } } - if !cx.shared.show_type_layout { - return; - } + display_fn(move |mut f| { + if !cx.shared.show_type_layout { + return Ok(()); + } - writeln!( - w, - "

\ - Layout§

" - ); - writeln!(w, "
"); + writeln!( + f, + "

\ + Layout§

" + )?; + writeln!(f, "
")?; - let tcx = cx.tcx(); - let param_env = tcx.param_env(ty_def_id); - let ty = tcx.type_of(ty_def_id).subst_identity(); - match tcx.layout_of(param_env.and(ty)) { - Ok(ty_layout) => { - writeln!( - w, - "

Note: Most layout information is \ - completely unstable and may even differ between compilations. \ - The only exception is types with certain repr(...) attributes. \ - Please see the Rust Reference’s \ - “Type Layout” \ - chapter for details on type layout guarantees.

" - ); - w.write_str("

Size: "); - write_size_of_layout(w, &ty_layout.layout.0, 0); - writeln!(w, "

"); - if let Variants::Multiple { variants, tag, tag_encoding, .. } = - &ty_layout.layout.variants() - { - if !variants.is_empty() { - w.write_str( - "

Size for each variant:

\ -
    ", - ); + let tcx = cx.tcx(); + let param_env = tcx.param_env(ty_def_id); + let ty = tcx.type_of(ty_def_id).subst_identity(); + match tcx.layout_of(param_env.and(ty)) { + Ok(ty_layout) => { + writeln!( + f, + "

    Note: Most layout information is \ + completely unstable and may even differ between compilations. \ + The only exception is types with certain repr(...) attributes. \ + Please see the Rust Reference’s \ + “Type Layout” \ + chapter for details on type layout guarantees.

    " + )?; + f.write_str("

    Size: ")?; + write_size_of_layout(&mut f, &ty_layout.layout.0, 0); + writeln!(f, "

    ")?; + if let Variants::Multiple { variants, tag, tag_encoding, .. } = + &ty_layout.layout.variants() + { + if !variants.is_empty() { + f.write_str( + "

    Size for each variant:

    \ +
      ", + )?; - let Adt(adt, _) = ty_layout.ty.kind() else { - span_bug!(tcx.def_span(ty_def_id), "not an adt") - }; + let Adt(adt, _) = ty_layout.ty.kind() else { + span_bug!(tcx.def_span(ty_def_id), "not an adt") + }; - let tag_size = if let TagEncoding::Niche { .. } = tag_encoding { - 0 - } else if let Primitive::Int(i, _) = tag.primitive() { - i.size().bytes() - } else { - span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int") - }; + let tag_size = if let TagEncoding::Niche { .. } = tag_encoding { + 0 + } else if let Primitive::Int(i, _) = tag.primitive() { + i.size().bytes() + } else { + span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int") + }; - for (index, layout) in variants.iter_enumerated() { - let name = adt.variant(index).name; - write!(w, "
    • {name}: "); - write_size_of_layout(w, layout, tag_size); - writeln!(w, "
    • "); + for (index, layout) in variants.iter_enumerated() { + let name = adt.variant(index).name; + write!(&mut f, "
    • {name}: ")?; + write_size_of_layout(&mut f, layout, tag_size); + writeln!(&mut f, "
    • ")?; + } + f.write_str("
    ")?; } - w.write_str("
"); } } + // This kind of layout error can occur with valid code, e.g. if you try to + // get the layout of a generic type such as `Vec`. + Err(LayoutError::Unknown(_)) => { + writeln!( + f, + "

Note: Unable to compute type layout, \ + possibly due to this type having generic parameters. \ + Layout can only be computed for concrete, fully-instantiated types.

" + )?; + } + // This kind of error probably can't happen with valid code, but we don't + // want to panic and prevent the docs from building, so we just let the + // user know that we couldn't compute the layout. + Err(LayoutError::SizeOverflow(_)) => { + writeln!( + f, + "

Note: Encountered an error during type layout; \ + the type was too big.

" + )?; + } + Err(LayoutError::NormalizationFailure(_, _)) => { + writeln!( + f, + "

Note: Encountered an error during type layout; \ + the type failed to be normalized.

" + )?; + } } - // This kind of layout error can occur with valid code, e.g. if you try to - // get the layout of a generic type such as `Vec`. - Err(LayoutError::Unknown(_)) => { - writeln!( - w, - "

Note: Unable to compute type layout, \ - possibly due to this type having generic parameters. \ - Layout can only be computed for concrete, fully-instantiated types.

" - ); - } - // This kind of error probably can't happen with valid code, but we don't - // want to panic and prevent the docs from building, so we just let the - // user know that we couldn't compute the layout. - Err(LayoutError::SizeOverflow(_)) => { - writeln!( - w, - "

Note: Encountered an error during type layout; \ - the type was too big.

" - ); - } - Err(LayoutError::NormalizationFailure(_, _)) => { - writeln!( - w, - "

Note: Encountered an error during type layout; \ - the type failed to be normalized.

" - ) - } - } - writeln!(w, "
"); + writeln!(f, "
") + }) } fn pluralize(count: usize) -> &'static str { diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html new file mode 100644 index 00000000000..a01457971c1 --- /dev/null +++ b/src/librustdoc/html/templates/item_union.html @@ -0,0 +1,23 @@ +

+    {{ self.render_attributes_in_pre() | safe }}
+    {{ self.render_union() | safe }}
+
+{{ self.document() | safe }} +{% 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 }} + + {% if let Some(stability_class) = self.stability_field(field) %} + + {% endif %} + {{ self.document_field(field) | safe }} + {% endfor %} +{% endif %} +{{ self.render_assoc_items() | safe }} +{{ self.document_type_layout() | safe }}