rustdoc: migrate item_union to an Askama template

This commit is contained in:
Michael Howell 2023-04-03 21:20:45 -07:00
parent 19f9f658d6
commit c325fda0bf
4 changed files with 382 additions and 291 deletions

View File

@ -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)
}

View File

@ -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::<Vec<_>>();
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,
"<h2 id=\"{id}\" class=\"small-section-header\">\
{title}\
<a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
);
)
.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, "<div id=\"trait-implementations-list\">{}</div>", impls);
write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations");
write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls).unwrap();
}
if !synthetic.is_empty() {
write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
w.write_str("<div id=\"synthetic-implementations-list\">");
render_impls(cx, w, synthetic, containing_item, false);
w.write_str("</div>");
write_impl_section_heading(
&mut w,
"Auto Trait Implementations",
"synthetic-implementations",
);
w.write_str("<div id=\"synthetic-implementations-list\">").unwrap();
render_impls(cx, &mut w, synthetic, containing_item, false);
w.write_str("</div>").unwrap();
}
if !blanket_impl.is_empty() {
write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
w.write_str("<div id=\"blanket-implementations-list\">");
render_impls(cx, w, blanket_impl, containing_item, false);
w.write_str("</div>");
write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations");
w.write_str("<div id=\"blanket-implementations-list\">").unwrap();
render_impls(cx, &mut w, blanket_impl, containing_item, false);
w.write_str("</div>").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, "<div id=\"{}\">", id);
w.push_buffer(impls_buf);
w.write_str("</div>");
write!(w, "{}", tmp_buf.into_inner()).unwrap();
write!(w, "<div id=\"{}\">", id).unwrap();
write!(w, "{}", impls_buf.into_inner()).unwrap();
w.write_str("</div>").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);
}
}
}

View File

@ -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,
"<details class=\"toggle type-contents-toggle\">\
@ -210,11 +210,12 @@ fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
<span>Show {}</span>\
</summary>",
text
);
)
.unwrap();
}
fn toggle_close(w: &mut Buffer) {
w.write_str("</details>");
fn toggle_close(mut w: impl fmt::Write) {
w.write_str("</details>").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<String> {
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,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
Fields<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>"
);
for (field, ty) in fields {
let name = field.name.expect("union field name");
let id = format!("{}.{}", ItemType::StructField, name);
write!(
w,
"<span id=\"{id}\" class=\"{shortty} small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{name}: {ty}</code>\
</span>",
shortty = ItemType::StructField,
ty = ty.print(cx),
);
if let Some(stability_class) = field.stability_class(cx.tcx()) {
write!(w, "<span class=\"stab {stability_class}\"></span>");
}
write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3));
fn fields_iter(
&self,
) -> std::iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>> {
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,
"<h2 id=\"variants\" class=\"variants small-section-header\">\
Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
</h2>",
document_non_exhaustive_header(it)
</h2>\
{}\
<div class=\"variants\">",
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
document_non_exhaustive(w, it);
write!(w, "<div class=\"variants\">");
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,
"<div class=\"sub-variant\" id=\"{variant_id}\">\
<h4>{heading}</h4>",
<h4>{heading}</h4>\
{}",
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, "</div>");
}
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,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>",
</h2>\
{}",
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<CtorKind>,
@ -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,
"<details class=\"toggle non-exhaustive\">\
<summary class=\"hideme\"><span>{}</span></summary>\
<div class=\"docblock\">",
{
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,
"<details class=\"toggle non-exhaustive\">\
<summary class=\"hideme\"><span>{}</span></summary>\
<div class=\"docblock\">",
{
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 <code>Struct { .. }</code> syntax; cannot be \
matched against without a wildcard <code>..</code>; 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 <code>Struct { .. }</code> syntax; cannot be \
matched against without a wildcard <code>..</code>; 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("</div></details>")?;
}
w.write_str("</div></details>");
}
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,
" (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
);
).unwrap();
}
}
}
if !cx.shared.show_type_layout {
return;
}
display_fn(move |mut f| {
if !cx.shared.show_type_layout {
return Ok(());
}
writeln!(
w,
"<h2 id=\"layout\" class=\"small-section-header\"> \
Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
);
writeln!(w, "<div class=\"docblock\">");
writeln!(
f,
"<h2 id=\"layout\" class=\"small-section-header\"> \
Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
)?;
writeln!(f, "<div class=\"docblock\">")?;
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,
"<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
<strong>completely unstable</strong> and may even differ between compilations. \
The only exception is types with certain <code>repr(...)</code> attributes. \
Please see the Rust References \
<a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
chapter for details on type layout guarantees.</p></div>"
);
w.write_str("<p><strong>Size:</strong> ");
write_size_of_layout(w, &ty_layout.layout.0, 0);
writeln!(w, "</p>");
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
&ty_layout.layout.variants()
{
if !variants.is_empty() {
w.write_str(
"<p><strong>Size for each variant:</strong></p>\
<ul>",
);
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,
"<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
<strong>completely unstable</strong> and may even differ between compilations. \
The only exception is types with certain <code>repr(...)</code> attributes. \
Please see the Rust References \
<a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
chapter for details on type layout guarantees.</p></div>"
)?;
f.write_str("<p><strong>Size:</strong> ")?;
write_size_of_layout(&mut f, &ty_layout.layout.0, 0);
writeln!(f, "</p>")?;
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
&ty_layout.layout.variants()
{
if !variants.is_empty() {
f.write_str(
"<p><strong>Size for each variant:</strong></p>\
<ul>",
)?;
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, "<li><code>{name}</code>: ");
write_size_of_layout(w, layout, tag_size);
writeln!(w, "</li>");
for (index, layout) in variants.iter_enumerated() {
let name = adt.variant(index).name;
write!(&mut f, "<li><code>{name}</code>: ")?;
write_size_of_layout(&mut f, layout, tag_size);
writeln!(&mut f, "</li>")?;
}
f.write_str("</ul>")?;
}
w.write_str("</ul>");
}
}
// 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<T>`.
Err(LayoutError::Unknown(_)) => {
writeln!(
f,
"<p><strong>Note:</strong> Unable to compute type layout, \
possibly due to this type having generic parameters. \
Layout can only be computed for concrete, fully-instantiated types.</p>"
)?;
}
// 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,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type was too big.</p>"
)?;
}
Err(LayoutError::NormalizationFailure(_, _)) => {
writeln!(
f,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type failed to be normalized.</p>"
)?;
}
}
// 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<T>`.
Err(LayoutError::Unknown(_)) => {
writeln!(
w,
"<p><strong>Note:</strong> Unable to compute type layout, \
possibly due to this type having generic parameters. \
Layout can only be computed for concrete, fully-instantiated types.</p>"
);
}
// 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,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type was too big.</p>"
);
}
Err(LayoutError::NormalizationFailure(_, _)) => {
writeln!(
w,
"<p><strong>Note:</strong> Encountered an error during type layout; \
the type failed to be normalized.</p>"
)
}
}
writeln!(w, "</div>");
writeln!(f, "</div>")
})
}
fn pluralize(count: usize) -> &'static str {

View File

@ -0,0 +1,23 @@
<pre class="rust item-decl"><code>
{{ self.render_attributes_in_pre() | safe }}
{{ self.render_union() | safe }}
</code></pre>
{{ self.document() | safe }}
{% if self.fields_iter().peek().is_some() %}
<h2 id="fields" class="fields small-section-header">
Fields<a href="#fields" class="anchor">§</a>
</h2>
{% for (field, ty) in self.fields_iter() %}
{% let name = field.name.expect("union field name") %}
<span id="structfield.{{ name }}" class="{{ ItemType::StructField }} small-section-header">
<a href="#structfield.{{ name }}" class="anchor field">§</a>
<code>{{ name }}: {{ self.print_ty(ty) | safe }}</code>
</span>
{% if let Some(stability_class) = self.stability_field(field) %}
<span class="stab {{ stability_class }}"></span>
{% endif %}
{{ self.document_field(field) | safe }}
{% endfor %}
{% endif %}
{{ self.render_assoc_items() | safe }}
{{ self.document_type_layout() | safe }}