From fc5de13d315c1b857ff53054fd68c4a928122330 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 3 Apr 2023 15:30:28 -0700 Subject: [PATCH 1/4] rustdoc: convert `print_tuple_struct_fields` to return a Display --- src/librustdoc/html/render/print_item.rs | 34 +++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 674cd0d62d49a..7837368f3405f 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1174,17 +1174,23 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: document_type_layout(w, cx, def_id); } -fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) { - for (i, ty) in s.iter().enumerate() { - if i > 0 { - w.write_str(", "); - } - match *ty.kind { - clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"), - clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)), - _ => unreachable!(), +fn print_tuple_struct_fields<'a, 'cx: 'a>( + cx: &'a Context<'cx>, + s: &'a [clean::Item], +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(|f| { + for (i, ty) in s.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + match *ty.kind { + clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?, + clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?, + _ => unreachable!(), + } } - } + Ok(()) + }) } fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { @@ -1221,9 +1227,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: clean::VariantItem(ref var) => match var.kind { clean::VariantKind::CLike => write!(w, "{}", name), clean::VariantKind::Tuple(ref s) => { - write!(w, "{}(", name); - print_tuple_struct_fields(w, cx, s); - w.write_str(")"); + write!(w, "{name}({})", print_tuple_struct_fields(cx, s),); } clean::VariantKind::Struct(ref s) => { render_struct(w, v, None, None, &s.fields, " ", false, cx); @@ -1276,9 +1280,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() }; if let clean::VariantKind::Tuple(ref s) = variant_data.kind { - w.write_str("("); - print_tuple_struct_fields(w, cx, s); - w.write_str(")"); + write!(w, "({})", print_tuple_struct_fields(cx, s),); } w.write_str(""); From 94faa5c7399202d9b3277e1851f34b3248675ec8 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 3 Apr 2023 15:35:27 -0700 Subject: [PATCH 2/4] rustdoc: convert render_attributes_in_pre to return a Display --- src/librustdoc/html/render/mod.rs | 17 ++++++++++---- src/librustdoc/html/render/print_item.rs | 30 ++++++++++++------------ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index d75d03071f892..920fbdcf0d616 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -50,6 +50,7 @@ use std::string::ToString; use askama::Template; use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; +use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; @@ -842,7 +843,7 @@ fn assoc_method( let (indent, indent_str, end_newline) = if parent == ItemType::Trait { header_len += 4; let indent_str = " "; - render_attributes_in_pre(w, meth, indent_str); + write!(w, "{}", render_attributes_in_pre(meth, indent_str)); (4, indent_str, Ending::NoNewline) } else { render_attributes_in_code(w, meth); @@ -1038,10 +1039,16 @@ fn attributes(it: &clean::Item) -> Vec { // When an attribute is rendered inside a `
` tag, it is formatted using
 // a whitespace prefix and newline.
-fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
-    for a in attributes(it) {
-        writeln!(w, "{}{}", prefix, a);
-    }
+fn render_attributes_in_pre<'a>(
+    it: &'a clean::Item,
+    prefix: &'a str,
+) -> impl fmt::Display + Captures<'a> {
+    crate::html::format::display_fn(move |f| {
+        for a in attributes(it) {
+            writeln!(f, "{}{}", prefix, a)?;
+        }
+        Ok(())
+    })
 }
 
 // When an attribute is rendered inside a  tag, it is formatted using
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 7837368f3405f..b223c49a89993 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -544,12 +544,12 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
         f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
 
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         w.reserve(header_len);
         write!(
             w,
-            "{vis}{constness}{asyncness}{unsafety}{abi}fn \
+            "{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
                 {name}{generics}{decl}{notable_traits}{where_clause}",
+            attrs = render_attributes_in_pre(it, ""),
             vis = visibility,
             constness = constness,
             asyncness = asyncness,
@@ -581,16 +581,16 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
 
     // Output the trait definition
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "{}{}{}trait {}{}{}",
+            "{attrs}{}{}{}trait {}{}{}",
             visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
             t.unsafety(tcx).print_with_space(),
             if t.is_auto(tcx) { "auto " } else { "" },
             it.name.unwrap(),
             t.generics.print(cx),
-            bounds
+            bounds,
+            attrs = render_attributes_in_pre(it, ""),
         );
 
         if !t.generics.where_predicates.is_empty() {
@@ -1057,14 +1057,14 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
 
 fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "trait {}{}{} = {};",
+            "{attrs}trait {}{}{} = {};",
             it.name.unwrap(),
             t.generics.print(cx),
             print_where_clause(&t.generics, cx, 0, Ending::Newline),
-            bounds(&t.bounds, true, cx)
+            bounds(&t.bounds, true, cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
     });
 
@@ -1079,14 +1079,14 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &
 
 fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "type {}{}{where_clause} = impl {bounds};",
+            "{attrs}type {}{}{where_clause} = impl {bounds};",
             it.name.unwrap(),
             t.generics.print(cx),
             where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
             bounds = bounds(&t.bounds, false, cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
     });
 
@@ -1102,15 +1102,15 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl
 fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
     fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
         wrap_item(w, |w| {
-            render_attributes_in_pre(w, it, "");
             write!(
                 w,
-                "{}type {}{}{where_clause} = {type_};",
+                "{attrs}{}type {}{}{where_clause} = {type_};",
                 visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
                 it.name.unwrap(),
                 t.generics.print(cx),
                 where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
                 type_ = t.type_.print(cx),
+                attrs = render_attributes_in_pre(it, ""),
             );
         });
     }
@@ -1130,7 +1130,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
 
 fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
+        write!(w, "{}", render_attributes_in_pre(it, ""));
         render_union(w, it, Some(&s.generics), &s.fields, cx);
     });
 
@@ -1197,13 +1197,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
     let tcx = cx.tcx();
     let count_variants = e.variants().count();
     wrap_item(w, |w| {
-        render_attributes_in_pre(w, it, "");
         write!(
             w,
-            "{}enum {}{}",
+            "{attrs}{}enum {}{}",
             visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
             it.name.unwrap(),
             e.generics.print(cx),
+            attrs = render_attributes_in_pre(it, ""),
         );
         if !print_where_clause_and_check(w, &e.generics, cx) {
             // If there wasn't a `where` clause, we add a whitespace.

From 19f9f658d64c4cb3eae674db677aabbe9b41cb0f Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Mon, 3 Apr 2023 17:12:19 -0700
Subject: [PATCH 3/4] rustdoc: migrate document functions to return a Display

---
 src/librustdoc/html/render/mod.rs        | 264 ++++++++++++-----------
 src/librustdoc/html/render/print_item.rs |  49 +++--
 src/librustdoc/html/sources.rs           |   6 +-
 3 files changed, 169 insertions(+), 150 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 920fbdcf0d616..49986433dfbbb 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -70,7 +70,7 @@ use crate::formats::item_type::ItemType;
 use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{
-    href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
+    display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
     print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
     Buffer, Ending, HrefError, PrintWithSpace,
 };
@@ -409,128 +409,134 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
     )
 }
 
-fn document(
-    w: &mut Buffer,
-    cx: &mut Context<'_>,
-    item: &clean::Item,
-    parent: Option<&clean::Item>,
+fn document<'a, 'cx: 'a>(
+    cx: &'a mut Context<'cx>,
+    item: &'a clean::Item,
+    parent: Option<&'a clean::Item>,
     heading_offset: HeadingOffset,
-) {
+) -> impl fmt::Display + 'a + Captures<'cx> {
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(cx, item, parent).render_into(w).unwrap();
-    if parent.is_none() {
-        document_full_collapsible(w, item, cx, heading_offset);
-    } else {
-        document_full(w, item, cx, heading_offset);
-    }
+
+    display_fn(move |f| {
+        document_item_info(cx, item, parent).render_into(f).unwrap();
+        if parent.is_none() {
+            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))?;
+        } else {
+            write!(f, "{}", document_full(item, cx, heading_offset))?;
+        }
+        Ok(())
+    })
 }
 
 /// Render md_text as markdown.
-fn render_markdown(
-    w: &mut Buffer,
-    cx: &mut Context<'_>,
-    md_text: &str,
+fn render_markdown<'a, 'cx: 'a>(
+    cx: &'a mut Context<'cx>,
+    md_text: &'a str,
     links: Vec,
     heading_offset: HeadingOffset,
-) {
-    write!(
-        w,
-        "
{}
", - Markdown { - content: md_text, - links: &links, - ids: &mut cx.id_map, - error_codes: cx.shared.codes, - edition: cx.shared.edition(), - playground: &cx.shared.playground, - heading_offset, - } - .into_string() - ) +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + write!( + f, + "
{}
", + Markdown { + content: md_text, + links: &links, + ids: &mut cx.id_map, + error_codes: cx.shared.codes, + edition: cx.shared.edition(), + playground: &cx.shared.playground, + heading_offset, + } + .into_string() + ) + }) } /// Writes a documentation block containing only the first paragraph of the documentation. If the /// docs are longer, a "Read more" link is appended to the end. -fn document_short( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, - link: AssocItemLink<'_>, - parent: &clean::Item, +fn document_short<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, + link: AssocItemLink<'a>, + parent: &'a clean::Item, show_def_docs: bool, -) { - document_item_info(cx, item, Some(parent)).render_into(w).unwrap(); - if !show_def_docs { - return; - } - if let Some(s) = item.doc_value() { - let (mut summary_html, has_more_content) = - MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); + if !show_def_docs { + return Ok(()); + } + if let Some(s) = item.doc_value() { + let (mut summary_html, has_more_content) = + MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); - if has_more_content { - let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx)); + if has_more_content { + let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx)); - if let Some(idx) = summary_html.rfind("

") { - summary_html.insert_str(idx, &link); - } else { - summary_html.push_str(&link); + if let Some(idx) = summary_html.rfind("

") { + summary_html.insert_str(idx, &link); + } else { + summary_html.push_str(&link); + } } - } - write!(w, "
{}
", summary_html,); - } + write!(f, "
{}
", summary_html)?; + } + Ok(()) + }) } -fn document_full_collapsible( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, +fn document_full_collapsible<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, heading_offset: HeadingOffset, -) { - document_full_inner(w, item, cx, true, heading_offset); +) -> impl fmt::Display + 'a + Captures<'cx> { + document_full_inner(item, cx, true, heading_offset) } -fn document_full( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, +fn document_full<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, heading_offset: HeadingOffset, -) { - document_full_inner(w, item, cx, false, heading_offset); +) -> impl fmt::Display + 'a + Captures<'cx> { + document_full_inner(item, cx, false, heading_offset) } -fn document_full_inner( - w: &mut Buffer, - item: &clean::Item, - cx: &mut Context<'_>, +fn document_full_inner<'a, 'cx: 'a>( + item: &'a clean::Item, + cx: &'a mut Context<'cx>, is_collapsible: bool, heading_offset: HeadingOffset, -) { - if let Some(s) = item.collapsed_doc_value() { - debug!("Doc block: =====\n{}\n=====", s); - if is_collapsible { - w.write_str( - "
\ - \ - Expand description\ - ", - ); - render_markdown(w, cx, &s, item.links(cx), heading_offset); - w.write_str("
"); - } else { - render_markdown(w, cx, &s, item.links(cx), heading_offset); +) -> impl fmt::Display + 'a + Captures<'cx> { + display_fn(move |f| { + if let Some(s) = item.collapsed_doc_value() { + debug!("Doc block: =====\n{}\n=====", s); + if is_collapsible { + write!( + f, + "
\ + \ + Expand description\ + {}
", + render_markdown(cx, &s, item.links(cx), heading_offset) + )?; + } else { + write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?; + } } - } - let kind = match &*item.kind { - clean::ItemKind::StrippedItem(box kind) | kind => kind, - }; + let kind = match &*item.kind { + clean::ItemKind::StrippedItem(box kind) | kind => kind, + }; - if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { - render_call_locations(w, cx, item); - } + if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { + render_call_locations(f, cx, item); + } + Ok(()) + }) } #[derive(Template)] @@ -1485,18 +1491,25 @@ fn render_impl( document_item_info(cx, it, Some(parent)) .render_into(&mut info_buffer) .unwrap(); - document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); + write!( + &mut doc_buffer, + "{}", + document_full(item, cx, HeadingOffset::H5) + ); short_documented = false; } else { // In case the item isn't documented, // provide short documentation from the trait. - document_short( + write!( &mut doc_buffer, - it, - cx, - link, - parent, - rendering_params.show_def_docs, + "{}", + document_short( + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ) ); } } @@ -1505,18 +1518,15 @@ fn render_impl( .render_into(&mut info_buffer) .unwrap(); if rendering_params.show_def_docs { - document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); + write!(&mut doc_buffer, "{}", document_full(item, cx, HeadingOffset::H5)); short_documented = false; } } } else { - document_short( + write!( &mut doc_buffer, - item, - cx, - link, - parent, - rendering_params.show_def_docs, + "{}", + document_short(item, cx, link, parent, rendering_params.show_def_docs,) ); } } @@ -2213,7 +2223,7 @@ const MAX_FULL_EXAMPLES: usize = 5; const NUM_VISIBLE_LINES: usize = 10; /// Generates the HTML for example call locations generated via the --scrape-examples flag. -fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) { +fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &clean::Item) { let tcx = cx.tcx(); let def_id = item.item_id.expect_def_id(); let key = tcx.def_path_hash(def_id); @@ -2222,7 +2232,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // Generate a unique ID so users can link to this section for a given method let id = cx.id_map.derive("scraped-examples"); write!( - w, + &mut w, "
\ \
\ @@ -2231,7 +2241,8 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
", root_path = cx.root_path(), id = id - ); + ) + .unwrap(); // Create a URL to a particular location in a reverse-dependency's source file let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) { @@ -2249,7 +2260,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite }; // Generate the HTML for a single example, being the title and code block - let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool { + let write_example = |mut w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool { let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { @@ -2297,7 +2308,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); write!( - w, + &mut w, "
\
\ {name} ({title})\ @@ -2310,10 +2321,12 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // The locations are encoded as a data attribute, so they can be read // later by the JS for interactions. locations = Escape(&locations_encoded) - ); + ) + .unwrap(); if line_ranges.len() > 1 { - write!(w, r#" "#); + write!(w, r#" "#) + .unwrap(); } // Look for the example file in the source map if it exists, otherwise return a dummy span @@ -2340,7 +2353,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite decoration_info.insert("highlight", byte_ranges); sources::print_src( - w, + &mut w, contents_subset, file_span, cx, @@ -2348,7 +2361,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min, needs_expansion }, ); - write!(w, "
"); + write!(w, "
").unwrap(); true }; @@ -2382,7 +2395,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // An example may fail to write if its source can't be read for some reason, so this method // continues iterating until a write succeeds - let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| { + let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| { while let Some(example) = it.next() { if write_example(&mut *w, example) { break; @@ -2391,7 +2404,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite }; // Write just one example that's visible by default in the method's description. - write_and_skip_failure(w, &mut it); + write_and_skip_failure(&mut w, &mut it); // Then add the remaining examples in a hidden section. if it.peek().is_some() { @@ -2404,17 +2417,19 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
Hide additional examples
\
\
" - ); + ) + .unwrap(); // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could // make the page arbitrarily huge! for _ in 0..MAX_FULL_EXAMPLES { - write_and_skip_failure(w, &mut it); + write_and_skip_failure(&mut w, &mut it); } // For the remaining examples, generate a
    containing links to the source files. if it.peek().is_some() { - write!(w, r#"").unwrap(); } - write!(w, "
"); + write!(w, "").unwrap(); } - write!(w, ""); + write!(w, "").unwrap(); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index b223c49a89993..ea53f8d107dcd 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -218,7 +218,7 @@ fn toggle_close(w: &mut Buffer) { } fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item, None, HeadingOffset::H2); + write!(w, "{}", document(cx, item, None, HeadingOffset::H2)); let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); @@ -562,7 +562,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle notable_traits = notable_traits.unwrap_or_default(), ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); } fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) { @@ -717,7 +717,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: }); // Trait documentation - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { write!( @@ -735,7 +735,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); let mut content = Buffer::empty_from(w); - document(&mut content, cx, m, Some(t), HeadingOffset::H5); + write!(&mut content, "{}", document(cx, m, Some(t), HeadingOffset::H5)); let toggled = !content.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; @@ -1068,7 +1068,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -1090,7 +1090,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -1117,7 +1117,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea write_content(w, cx, it, t); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let def_id = it.item_id.expect_def_id(); // Render any items associated directly to this alias, as otherwise they @@ -1134,7 +1134,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: render_union(w, it, Some(&s.generics), &s.fields, cx); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let mut fields = s .fields @@ -1166,7 +1166,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: if let Some(stability_class) = field.stability_class(cx.tcx()) { write!(w, ""); } - document(w, cx, field, Some(it), HeadingOffset::H3); + write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); } } let def_id = it.item_id.expect_def_id(); @@ -1248,7 +1248,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: } }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); if count_variants != 0 { write!( @@ -1324,10 +1324,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: {f}: {t}\ ", f = field.name.unwrap(), - t = ty.print(cx) + t = ty.print(cx), + ); + write!( + w, + "{}", + document(cx, field, Some(variant), HeadingOffset::H5) ); - document(w, cx, field, Some(variant), HeadingOffset::H5); - write!(w, ""); } _ => unreachable!(), } @@ -1335,7 +1338,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str(""); } - document(w, cx, variant, Some(it), HeadingOffset::H4); + write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); } write!(w, ""); } @@ -1346,7 +1349,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { highlight::render_item_decl_with_highlighting(&t.source, w); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { @@ -1372,12 +1375,12 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c } } }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); - document(w, cx, it, None, HeadingOffset::H2); + 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); } else { @@ -1435,7 +1438,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle } }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { @@ -1444,7 +1447,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let mut fields = s .fields @@ -1478,7 +1481,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean item_type = ItemType::StructField, ty = ty.print(cx) ); - document(w, cx, field, Some(it), HeadingOffset::H3); + write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); } } } @@ -1499,7 +1502,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean typ = s.type_.print(cx) ); }); - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1514,13 +1517,13 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { ); }); - document(w, cx, it, None, HeadingOffset::H2); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { - document(w, cx, it, None, HeadingOffset::H2) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 1d298f52f7588..c8397967c8791 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -2,7 +2,6 @@ use crate::clean; use crate::docfs::PathError; use crate::error::Error; use crate::html::format; -use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; use crate::html::render::Context; @@ -17,6 +16,7 @@ use rustc_span::source_map::FileName; use std::cell::RefCell; use std::ffi::OsStr; +use std::fmt; use std::fs; use std::ops::RangeInclusive; use std::path::{Component, Path, PathBuf}; @@ -294,7 +294,7 @@ pub(crate) enum SourceContext { /// Wrapper struct to render the source code of a file. This will do things like /// adding line numbers to the left-hand side. pub(crate) fn print_src( - buf: &mut Buffer, + mut writer: impl fmt::Write, s: &str, file_span: rustc_span::Span, context: &Context<'_>, @@ -329,5 +329,5 @@ pub(crate) fn print_src( ); Ok(()) }); - Source { embedded, needs_expansion, lines, code_html: code }.render_into(buf).unwrap(); + Source { embedded, needs_expansion, lines, code_html: code }.render_into(&mut writer).unwrap(); } From c325fda0bfb32affaeba76c360779585278b8c61 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 3 Apr 2023 21:20:45 -0700 Subject: [PATCH 4/4] 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 | 573 ++++++++++-------- src/librustdoc/html/templates/item_union.html | 23 + 4 files changed, 384 insertions(+), 293 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 0895bb510d481..77c2cd5b425d8 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 49986433dfbbb..1e3cd2668506f 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 ea53f8d107dcd..6bce57340040b 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(), - ); - - let where_displayed = g - .map(|g| { - write!(w, "{}", g.print(cx)); - print_where_clause_and_check(w, g, cx) - }) - .unwrap_or(false); +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| { + 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(" "); - } + // If there wasn't a `where` clause, we add a whitespace. + if !where_displayed { + f.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)); - } + 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!( - w, - " {}{}: {},\n", - visibility_print_with_space(field.visibility(tcx), field.item_id, cx), - field.name.unwrap(), - ty.print(cx) - ); + 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!(w, " /* private fields */\n"); - } - if toggle { - toggle_close(w); - } - w.write_str("}"); + 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; - } - - writeln!( - w, - "

\ - Layout§

" - ); - writeln!(w, "
"); - - 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 Adt(adt, _) = ty_layout.ty.kind() else { - span_bug!(tcx.def_span(ty_def_id), "not an adt") - }; + display_fn(move |mut f| { + if !cx.shared.show_type_layout { + return Ok(()); + } - 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") - }; + writeln!( + f, + "

    \ + Layout§

    " + )?; + writeln!(f, "
    ")?; - 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, "
  • "); + 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 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!(&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 0000000000000..a01457971c178 --- /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 }}