diff options
Diffstat (limited to 'src/librustdoc/html/render/print_item.rs')
-rw-r--r-- | src/librustdoc/html/render/print_item.rs | 267 |
1 files changed, 171 insertions, 96 deletions
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 62027a3fa..383e3c170 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -9,7 +9,6 @@ use rustc_middle::middle::stability; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; -use std::borrow::Borrow; use std::cell::{RefCell, RefMut}; use std::cmp::Ordering; use std::fmt; @@ -40,6 +39,110 @@ use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; +/// Generates an Askama template struct for rendering items with common methods. +/// +/// Usage: +/// ```ignore (illustrative) +/// item_template!( +/// #[template(path = "<template.html>", /* additional values */)] +/// /* additional meta items */ +/// struct MyItem<'a, 'cx> { +/// cx: RefCell<&'a mut Context<'cx>>, +/// it: &'a clean::Item, +/// /* additional fields */ +/// }, +/// methods = [ /* method names (comma separated; refer to macro definition of `item_template_methods!()`) */ ] +/// ) +/// ``` +/// +/// NOTE: ensure that the generic lifetimes (`'a`, `'cx`) and +/// required fields (`cx`, `it`) are identical (in terms of order and definition). +macro_rules! item_template { + ( + $(#[$meta:meta])* + struct $name:ident<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + $($field_name:ident: $field_ty:ty),*, + }, + methods = [$($methods:tt),* $(,)?] + ) => { + #[derive(Template)] + $(#[$meta])* + struct $name<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + $($field_name: $field_ty),* + } + + impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> { + fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) { + (&self.it, self.cx.borrow_mut()) + } + } + + impl<'a, 'cx: 'a> $name<'a, 'cx> { + item_template_methods!($($methods)*); + } + }; +} + +/// Implement common methods for item template structs generated by `item_template!()`. +/// +/// NOTE: this macro is intended to be used only by `item_template!()`. +macro_rules! item_template_methods { + () => {}; + (document $($rest:tt)*) => { + fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, mut cx) = self.item_and_mut_cx(); + let v = document(*cx, item, None, HeadingOffset::H2); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (document_type_layout $($rest:tt)*) => { + fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, cx) = self.item_and_mut_cx(); + let def_id = item.item_id.expect_def_id(); + let v = document_type_layout(*cx, def_id); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (render_attributes_in_pre $($rest:tt)*) => { + fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, cx) = self.item_and_mut_cx(); + let tcx = cx.tcx(); + let v = render_attributes_in_pre(item, "", tcx); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + (render_assoc_items $($rest:tt)*) => { + fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { + display_fn(move |f| { + let (item, mut cx) = self.item_and_mut_cx(); + let def_id = item.item_id.expect_def_id(); + let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All); + write!(f, "{v}") + }) + } + item_template_methods!($($rest)*); + }; + ($method:ident $($rest:tt)*) => { + compile_error!(concat!("unknown method: ", stringify!($method))); + }; + ($token:tt $($rest:tt)*) => { + compile_error!(concat!("unexpected token: ", stringify!($token))); + }; +} + const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">"; const ITEM_TABLE_CLOSE: &str = "</ul>"; const ITEM_TABLE_ROW_OPEN: &str = "<li>"; @@ -222,49 +325,6 @@ trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display { fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>); } -fn item_template_document<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, mut cx) = templ.item_and_mut_cx(); - let v = document(*cx, item, None, HeadingOffset::H2); - write!(f, "{v}") - }) -} - -fn item_template_document_type_layout<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, cx) = templ.item_and_mut_cx(); - let def_id = item.item_id.expect_def_id(); - let v = document_type_layout(*cx, def_id); - write!(f, "{v}") - }) -} - -fn item_template_render_attributes_in_pre<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, cx) = templ.item_and_mut_cx(); - let tcx = cx.tcx(); - let v = render_attributes_in_pre(item, "", tcx); - write!(f, "{v}") - }) -} - -fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>( - templ: &'b impl ItemTemplate<'a, 'cx>, -) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - display_fn(move |f| { - let (item, mut cx) = templ.item_and_mut_cx(); - let def_id = item.item_id.expect_def_id(); - let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All); - write!(f, "{v}") - }) -} - fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { write!(w, "{}", document(cx, item, None, HeadingOffset::H2)); @@ -587,8 +647,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle + name.as_str().len() + generics_len; - let notable_traits = - f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx)); + let notable_traits = notable_traits_button(&f.decl.output, cx); wrap_item(w, |w| { w.reserve(header_len); @@ -1102,7 +1161,12 @@ 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) { +fn item_trait_alias( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::TraitAlias, +) { wrap_item(w, |w| { write!( w, @@ -1112,19 +1176,25 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds(&t.bounds, true, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // 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 // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + .unwrap(); } -fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { +fn item_opaque_ty( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::OpaqueTy, +) { wrap_item(w, |w| { write!( w, @@ -1134,16 +1204,18 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // 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 // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + .unwrap(); } fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { @@ -1176,19 +1248,15 @@ 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) { - #[derive(Template)] - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: RefCell<&'a mut Context<'cx>>, - it: &'a clean::Item, - s: &'a clean::Union, - } - - impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for ItemUnion<'a, 'cx> { - fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) { - (self.it, self.cx.borrow_mut()) - } - } + item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: RefCell<&'a mut Context<'cx>>, + it: &'a clean::Item, + s: &'a clean::Union, + }, + methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] + ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { @@ -1198,6 +1266,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: write!(f, "{v}") }) } + fn document_field<'b>( &'b self, field: &'a clean::Item, @@ -1208,10 +1277,12 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: 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, @@ -1420,37 +1491,41 @@ fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) } -fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - wrap_item(w, |w| { +fn item_proc_macro( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + m: &clean::ProcMacro, +) { + wrap_item(w, |buffer| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(w, "{}!() {{ /* proc-macro */ }}", name); + write!(buffer, "{name}!() {{ /* proc-macro */ }}").unwrap(); } MacroKind::Attr => { - write!(w, "#[{}]", name); + write!(buffer, "#[{name}]").unwrap(); } MacroKind::Derive => { - write!(w, "#[derive({})]", name); + write!(buffer, "#[derive({name})]").unwrap(); if !m.helpers.is_empty() { - w.push_str("\n{\n"); - w.push_str(" // Attributes available to this derive:\n"); + buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap(); for attr in &m.helpers { - writeln!(w, " #[{}]", attr); + writeln!(buffer, " #[{attr}]").unwrap(); } - w.push_str("}\n"); + buffer.write_str("}\n").unwrap(); } } } }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } -fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap(); } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. @@ -1560,8 +1635,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { + wrap_item(w, |buffer| { render_attributes_in_code(buffer, it, cx.tcx()); write!( buffer, @@ -1570,29 +1644,29 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, mutability = s.mutability.print_with_space(), name = it.name.unwrap(), typ = s.type_.print(cx) - ); + ) + .unwrap(); }); - write!(w, "{}", buffer.into_inner()).unwrap(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } -fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { - wrap_item(w, |w| { - w.write_str("extern {\n"); - render_attributes_in_code(w, it, cx.tcx()); +fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { + wrap_item(w, |buffer| { + buffer.write_str("extern {\n").unwrap(); + render_attributes_in_code(buffer, it, cx.tcx()); write!( - w, + buffer, " {}type {};\n}}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), it.name.unwrap(), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + .unwrap(); } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1666,13 +1740,14 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_item<F>(w: &mut Buffer, f: F) +fn wrap_item<W, F>(w: &mut W, f: F) where - F: FnOnce(&mut Buffer), + W: fmt::Write, + F: FnOnce(&mut W), { - w.write_str(r#"<pre class="rust item-decl"><code>"#); + write!(w, r#"<pre class="rust item-decl"><code>"#).unwrap(); f(w); - w.write_str("</code></pre>"); + write!(w, "</code></pre>").unwrap(); } #[derive(PartialEq, Eq)] |