summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/render/print_item.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/render/print_item.rs')
-rw-r--r--src/librustdoc/html/render/print_item.rs267
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)]