diff options
| author | Ralf Jung <post@ralfj.de> | 2025-03-06 05:58:06 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-06 05:58:06 +0000 |
| commit | b178f22bd8267155f2e330731a53ef3e4eac28bb (patch) | |
| tree | f861f7c201007112dc2f2f016855c7e0529e9cc0 /src/librustdoc/html | |
| parent | 14cfc3ade4538ecf6f684962521685664348b522 (diff) | |
| parent | f80cac723acf10a8f9fd05b335ed5797e4f69a1a (diff) | |
| download | rust-b178f22bd8267155f2e330731a53ef3e4eac28bb.tar.gz rust-b178f22bd8267155f2e330731a53ef3e4eac28bb.zip | |
Merge pull request #4220 from rust-lang/rustup-2025-03-06
Automatic Rustup
Diffstat (limited to 'src/librustdoc/html')
| -rw-r--r-- | src/librustdoc/html/format.rs | 160 | ||||
| -rw-r--r-- | src/librustdoc/html/render/context.rs | 5 | ||||
| -rw-r--r-- | src/librustdoc/html/render/mod.rs | 1483 | ||||
| -rw-r--r-- | src/librustdoc/html/render/print_item.rs | 3184 | ||||
| -rw-r--r-- | src/librustdoc/html/render/type_layout.rs | 6 | ||||
| -rw-r--r-- | src/librustdoc/html/render/write_shared.rs | 38 | ||||
| -rw-r--r-- | src/librustdoc/html/sources.rs | 2 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/rustdoc.d.ts | 17 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/search.js | 69 |
9 files changed, 2506 insertions, 2458 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 91b4b3ba1eb..ea740508c58 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -15,7 +15,6 @@ use std::iter::{self, once}; use itertools::Either; use rustc_abi::ExternAbi; use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -30,7 +29,7 @@ use super::url_parts_builder::{UrlPartsBuilder, estimate_item_path_byte_length}; use crate::clean::types::ExternalLocation; use crate::clean::utils::find_nearest_parent_module; use crate::clean::{self, ExternalCrate, PrimitiveType}; -use crate::display::Joined as _; +use crate::display::{Joined as _, MaybeDisplay as _}; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyText}; @@ -41,10 +40,10 @@ pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { s.write_fmt(f).unwrap(); } -pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( - bounds: &'a [clean::GenericBound], - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn print_generic_bounds( + bounds: &[clean::GenericBound], + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { let mut bounds_dup = FxHashSet::default(); @@ -57,10 +56,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( } impl clean::GenericParamDef { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { write!(f, "{}", self.name)?; @@ -80,7 +76,7 @@ impl clean::GenericParamDef { print_generic_bounds(bounds, cx).fmt(f)?; } - if let Some(ref ty) = default { + if let Some(ty) = default { f.write_str(" = ")?; ty.print(cx).fmt(f)?; } @@ -107,10 +103,7 @@ impl clean::GenericParamDef { } impl clean::Generics { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); if real_params.peek().is_none() { @@ -134,10 +127,7 @@ pub(crate) enum Ending { NoNewline, } -fn print_where_predicate<'a, 'tcx: 'a>( - predicate: &'a clean::WherePredicate, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match predicate { clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { @@ -173,17 +163,17 @@ fn print_where_predicate<'a, 'tcx: 'a>( /// * The Generics from which to emit a where-clause. /// * The number of spaces to indent each line with. /// * Whether the where-clause needs to add a comma and newline after the last bound. -pub(crate) fn print_where_clause<'a, 'tcx: 'a>( - gens: &'a clean::Generics, - cx: &'a Context<'tcx>, +pub(crate) fn print_where_clause( + gens: &clean::Generics, + cx: &Context<'_>, indent: usize, ending: Ending, -) -> impl Display + 'a + Captures<'tcx> { - fmt::from_fn(move |f| { - if gens.where_predicates.is_empty() { - return Ok(()); - } +) -> Option<impl Display> { + if gens.where_predicates.is_empty() { + return None; + } + Some(fmt::from_fn(move |f| { let where_preds = fmt::from_fn(|f| { gens.where_predicates .iter() @@ -246,17 +236,17 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( } }; write!(f, "{clause}") - }) + })) } impl clean::Lifetime { - pub(crate) fn print(&self) -> impl Display + '_ { + pub(crate) fn print(&self) -> impl Display { self.0.as_str() } } impl clean::ConstantKind { - pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ { + pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display { let expr = self.expr(tcx); fmt::from_fn(move |f| { if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } @@ -265,7 +255,7 @@ impl clean::ConstantKind { } impl clean::PolyTrait { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?; self.trait_.print(cx).fmt(f) @@ -274,10 +264,7 @@ impl clean::PolyTrait { } impl clean::GenericBound { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifiers) => { @@ -304,7 +291,7 @@ impl clean::GenericBound { } impl clean::GenericArgs { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, constraints } => { @@ -809,11 +796,11 @@ fn primitive_link_fragment( Ok(()) } -fn tybounds<'a, 'tcx: 'a>( - bounds: &'a [clean::PolyTrait], - lt: &'a Option<clean::Lifetime>, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn tybounds( + bounds: &[clean::PolyTrait], + lt: &Option<clean::Lifetime>, + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?; if let Some(lt) = lt { @@ -825,11 +812,11 @@ fn tybounds<'a, 'tcx: 'a>( }) } -fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( - params: &'a [clean::GenericParamDef], - cx: &'a Context<'tcx>, +fn print_higher_ranked_params_with_space( + params: &[clean::GenericParamDef], + cx: &Context<'_>, keyword: &'static str, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { if !params.is_empty() { f.write_str(keyword)?; @@ -841,11 +828,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( }) } -pub(crate) fn anchor<'a: 'cx, 'cx>( - did: DefId, - text: Symbol, - cx: &'cx Context<'a>, -) -> impl Display + Captures<'a> + 'cx { +pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let parts = href(did, cx); if let Ok((url, short_ty, fqp)) = parts { @@ -1121,29 +1104,19 @@ fn fmt_type( } impl clean::Type { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Path { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) } } impl clean::Impl { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - use_absolute: bool, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str("impl")?; self.generics.print(cx).fmt(f)?; @@ -1179,15 +1152,15 @@ impl clean::Impl { self.print_type(&self.for_, f, use_absolute, cx)?; } - print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f) + print_where_clause(&self.generics, cx, 0, Ending::Newline).maybe_display().fmt(f) }) } - fn print_type<'a, 'tcx: 'a>( + fn print_type( &self, type_: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cx: &'a Context<'tcx>, + cx: &Context<'_>, ) -> Result<(), fmt::Error> { if let clean::Type::Tuple(types) = type_ && let [clean::Type::Generic(name)] = &types[..] @@ -1258,10 +1231,7 @@ impl clean::Impl { } impl clean::Arguments { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { self.values .iter() @@ -1301,10 +1271,7 @@ impl Display for Indent { } impl clean::FnDecl { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { @@ -1333,12 +1300,12 @@ impl clean::FnDecl { /// are preserved. /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is /// necessary. - pub(crate) fn full_print<'a, 'tcx: 'a>( - &'a self, + pub(crate) fn full_print( + &self, header_len: usize, indent: usize, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + cx: &Context<'_>, + ) -> impl Display { fmt::from_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); @@ -1420,10 +1387,7 @@ impl clean::FnDecl { self.print_output(cx).fmt(f) } - fn print_output<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + fn print_output(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { @@ -1434,10 +1398,7 @@ impl clean::FnDecl { } } -pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( - item: &clean::Item, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display { use std::fmt::Write as _; let vis: Cow<'static, str> = match item.visibility(cx.tcx()) { None => "".into(), @@ -1546,10 +1507,7 @@ pub(crate) fn print_constness_with_space( } impl clean::Import { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { @@ -1570,10 +1528,7 @@ impl clean::Import { } impl clean::ImportSource { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.did { Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { @@ -1593,10 +1548,7 @@ impl clean::ImportSource { } impl clean::AssocItemConstraint { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str(self.assoc.name.as_str())?; self.assoc.args.print(cx).fmt(f)?; @@ -1627,15 +1579,12 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space<'a>(v: bool) -> &'a str { +pub(crate) fn print_default_space(v: bool) -> &'static str { if v { "default " } else { "" } } impl clean::GenericArg { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), @@ -1646,10 +1595,7 @@ impl clean::GenericArg { } impl clean::Term { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::Term::Type(ty) => ty.print(cx).fmt(f), clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 146bdd34069..5f69e79f3ab 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -237,8 +237,7 @@ impl<'tcx> Context<'tcx> { }; if !render_redirect_pages { - let mut page_buffer = String::new(); - print_item(self, it, &mut page_buffer); + let content = print_item(self, it); let page = layout::Page { css_class: tyname_s, root_path: &self.root_path(), @@ -254,7 +253,7 @@ impl<'tcx> Context<'tcx> { BufDisplay(|buf: &mut String| { print_sidebar(self, it, buf); }), - page_buffer, + content, &self.shared.style_files, ) } else { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 204631063a2..b2ad2fa773a 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -38,7 +38,7 @@ mod type_layout; mod write_shared; use std::collections::VecDeque; -use std::fmt::{self, Write}; +use std::fmt::{self, Display as _, Write}; use std::iter::Peekable; use std::path::PathBuf; use std::{fs, str}; @@ -47,7 +47,6 @@ use rinja::Template; use rustc_attr_parsing::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -82,7 +81,7 @@ use crate::html::{highlight, sources}; use crate::scrape_examples::{CallData, CallLocation}; use crate::{DOC_RUST_LANG_ORG_VERSION, try_none}; -pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { +pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { fmt::from_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) } }) @@ -310,7 +309,7 @@ impl ItemEntry { } impl ItemEntry { - pub(crate) fn print(&self) -> impl fmt::Display + '_ { + pub(crate) fn print(&self) -> impl fmt::Display { fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name))) } } @@ -505,12 +504,12 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { ) } -fn document<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - item: &'a clean::Item, - parent: Option<&'a clean::Item>, +fn document( + cx: &Context<'_>, + item: &clean::Item, + parent: Option<&clean::Item>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { if let Some(ref name) = item.name { info!("Documenting {name}"); } @@ -526,12 +525,12 @@ fn document<'a, 'cx: 'a>( } /// Render md_text as markdown. -fn render_markdown<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - md_text: &'a str, +fn render_markdown( + cx: &Context<'_>, + md_text: &str, links: Vec<RenderedLink>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { write!( f, @@ -552,13 +551,13 @@ fn render_markdown<'a, 'cx: 'a>( /// 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<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, - link: AssocItemLink<'a>, - parent: &'a clean::Item, +fn document_short( + item: &clean::Item, + cx: &Context<'_>, + link: AssocItemLink<'_>, + parent: &clean::Item, show_def_docs: bool, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); if !show_def_docs { @@ -595,28 +594,28 @@ fn document_short<'a, 'cx: 'a>( }) } -fn document_full_collapsible<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_collapsible( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, true, heading_offset) } -fn document_full<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, false, heading_offset) } -fn document_full_inner<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_inner( + item: &clean::Item, + cx: &Context<'_>, is_collapsible: bool, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { if let Some(s) = item.opt_doc_value() { debug!("Doc block: =====\n{s}\n====="); @@ -774,9 +773,7 @@ pub(crate) fn render_impls( let did = i.trait_did().unwrap(); let provided_trait_methods = i.inner_impl().provided_trait_methods(cx.tcx()); let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods); - let mut buffer = String::new(); - render_impl( - &mut buffer, + let imp = render_impl( cx, i, containing_item, @@ -791,7 +788,7 @@ pub(crate) fn render_impls( toggle_open_by_default, }, ); - buffer + imp.to_string() }) .collect::<Vec<_>>(); rendered_impls.sort(); @@ -799,11 +796,11 @@ pub(crate) fn render_impls( } /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. -fn assoc_href_attr<'a, 'tcx>( +fn assoc_href_attr( it: &clean::Item, - link: AssocItemLink<'a>, - cx: &Context<'tcx>, -) -> Option<impl fmt::Display + 'a + Captures<'tcx>> { + link: AssocItemLink<'_>, + cx: &Context<'_>, +) -> Option<impl fmt::Display> { let name = it.name.unwrap(); let item_type = it.type_(); @@ -814,7 +811,7 @@ fn assoc_href_attr<'a, 'tcx>( } let href = match link { - AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id), + AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id), AssocItemLink::Anchor(None) => Href::Anchor(item_type), AssocItemLink::GotoSource(did, provided_methods) => { // We're creating a link from the implementation of an associated item to its @@ -880,7 +877,6 @@ enum AssocConstValue<'a> { } fn assoc_const( - w: &mut String, it: &clean::Item, generics: &clean::Generics, ty: &clean::Type, @@ -888,11 +884,11 @@ fn assoc_const( link: AssocItemLink<'_>, indent: usize, cx: &Context<'_>, -) { +) -> impl fmt::Display { let tcx = cx.tcx(); - write_str( - w, - format_args!( + fmt::from_fn(move |w| { + write!( + w, "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}", indent = " ".repeat(indent), vis = visibility_print_with_space(it, cx), @@ -900,28 +896,27 @@ fn assoc_const( name = it.name.as_ref().unwrap(), generics = generics.print(cx), ty = ty.print(cx), - ), - ); - if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value { - // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the - // hood which adds noisy underscores and a type suffix to number literals. - // This hurts readability in this context especially when more complex expressions - // are involved and it doesn't add much of value. - // Find a way to print constants here without all that jazz. - let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx)); - if match value { - AssocConstValue::TraitDefault(_) => true, // always show - AssocConstValue::Impl(_) => repr != "_", // show if there is a meaningful value to show - AssocConstValue::None => unreachable!(), - } { - write_str(w, format_args!(" = {}", Escape(&repr))); + )?; + if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value { + // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the + // hood which adds noisy underscores and a type suffix to number literals. + // This hurts readability in this context especially when more complex expressions + // are involved and it doesn't add much of value. + // Find a way to print constants here without all that jazz. + let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx)); + if match value { + AssocConstValue::TraitDefault(_) => true, // always show + AssocConstValue::Impl(_) => repr != "_", // show if there is a meaningful value to show + AssocConstValue::None => unreachable!(), + } { + write!(w, " = {}", Escape(&repr))?; + } } - } - write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline))); + write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display()) + }) } fn assoc_type( - w: &mut String, it: &clean::Item, generics: &clean::Generics, bounds: &[clean::GenericBound], @@ -929,30 +924,29 @@ fn assoc_type( link: AssocItemLink<'_>, indent: usize, cx: &Context<'_>, -) { - write_str( - w, - format_args!( +) -> impl fmt::Display { + fmt::from_fn(move |w| { + write!( + w, "{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}", indent = " ".repeat(indent), vis = visibility_print_with_space(it, cx), href = assoc_href_attr(it, link, cx).maybe_display(), name = it.name.as_ref().unwrap(), generics = generics.print(cx), - ), - ); - if !bounds.is_empty() { - write_str(w, format_args!(": {}", print_generic_bounds(bounds, cx))); - } - // Render the default before the where-clause which aligns with the new recommended style. See #89122. - if let Some(default) = default { - write_str(w, format_args!(" = {}", default.print(cx))); - } - write_str(w, format_args!("{}", print_where_clause(generics, cx, indent, Ending::NoNewline))); + )?; + if !bounds.is_empty() { + write!(w, ": {}", print_generic_bounds(bounds, cx))?; + } + // Render the default before the where-clause which aligns with the new recommended style. See #89122. + if let Some(default) = default { + write!(w, " = {}", default.print(cx))?; + } + write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display()) + }) } fn assoc_method( - w: &mut String, meth: &clean::Item, g: &clean::Generics, d: &clean::FnDecl, @@ -960,7 +954,7 @@ fn assoc_method( parent: ItemType, cx: &Context<'_>, render_mode: RenderMode, -) { +) -> impl fmt::Display { let tcx = cx.tcx(); let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); @@ -976,61 +970,53 @@ fn assoc_method( ), RenderMode::ForDeref { .. } => "", }; - let asyncness = header.asyncness.print_with_space(); - let safety = header.safety.print_with_space(); - let abi = print_abi_with_space(header.abi).to_string(); - let href = assoc_href_attr(meth, link, cx).maybe_display(); - - // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. - let generics_len = format!("{:#}", g.print(cx)).len(); - let mut header_len = "fn ".len() - + vis.len() - + defaultness.len() - + constness.len() - + asyncness.len() - + safety.len() - + abi.len() - + name.as_str().len() - + generics_len; - - let notable_traits = notable_traits_button(&d.output, cx).maybe_display(); - - let (indent, indent_str, end_newline) = if parent == ItemType::Trait { - header_len += 4; - let indent_str = " "; - write_str(w, format_args!("{}", render_attributes_in_pre(meth, indent_str, cx))); - (4, indent_str, Ending::NoNewline) - } else { - render_attributes_in_code(w, meth, cx); - (0, "", Ending::Newline) - }; - w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len()); - write_str( - w, - format_args!( + + fmt::from_fn(move |w| { + let asyncness = header.asyncness.print_with_space(); + let safety = header.safety.print_with_space(); + let abi = print_abi_with_space(header.abi).to_string(); + let href = assoc_href_attr(meth, link, cx).maybe_display(); + + // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. + let generics_len = format!("{:#}", g.print(cx)).len(); + let mut header_len = "fn ".len() + + vis.len() + + defaultness.len() + + constness.len() + + asyncness.len() + + safety.len() + + abi.len() + + name.as_str().len() + + generics_len; + + let notable_traits = notable_traits_button(&d.output, cx).maybe_display(); + + let (indent, indent_str, end_newline) = if parent == ItemType::Trait { + header_len += 4; + let indent_str = " "; + write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx))?; + (4, indent_str, Ending::NoNewline) + } else { + render_attributes_in_code(w, meth, cx); + (0, "", Ending::Newline) + }; + write!( + w, "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \ - <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}", + <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}", indent = indent_str, - vis = vis, - defaultness = defaultness, - constness = constness, - asyncness = asyncness, - safety = safety, - abi = abi, - href = href, - name = name, generics = g.print(cx), decl = d.full_print(header_len, indent, cx), - where_clause = print_where_clause(g, cx, indent, end_newline), - ), - ); + where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(), + ) + }) } /// Writes a span containing the versions at which an item became stable and/or const-stable. For /// example, if the item became stable at 1.0.0, and const-stable at 1.45.0, this function would /// write a span containing "1.0.0 (const: 1.45.0)". /// -/// Returns `true` if a stability annotation was rendered. +/// Returns `None` if there is no stability annotation to be rendered. /// /// Stability and const-stability are considered separately. If the item is unstable, no version /// will be written. If the item is const-unstable, "const: unstable" will be appended to the @@ -1041,11 +1027,10 @@ fn assoc_method( /// will include the const-stable version, but no stable version will be emitted, as a natural /// consequence of the above rules. fn render_stability_since_raw_with_extra( - w: &mut String, stable_version: Option<StableSince>, const_stability: Option<ConstStability>, extra_class: &str, -) -> bool { +) -> Option<impl fmt::Display> { let mut title = String::new(); let mut stability = String::new(); @@ -1095,14 +1080,9 @@ fn render_stability_since_raw_with_extra( } } - if !stability.is_empty() { - write_str( - w, - format_args!(r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#), - ); - } - - !stability.is_empty() + (!stability.is_empty()).then_some(fmt::from_fn(move |w| { + write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#) + })) } fn since_to_string(since: &StableSince) -> Option<String> { @@ -1115,31 +1095,25 @@ fn since_to_string(since: &StableSince) -> Option<String> { #[inline] fn render_stability_since_raw( - w: &mut String, ver: Option<StableSince>, const_stability: Option<ConstStability>, -) -> bool { - render_stability_since_raw_with_extra(w, ver, const_stability, "") +) -> Option<impl fmt::Display> { + render_stability_since_raw_with_extra(ver, const_stability, "") } fn render_assoc_item( - w: &mut String, item: &clean::Item, link: AssocItemLink<'_>, parent: ItemType, cx: &Context<'_>, render_mode: RenderMode, -) { - match &item.kind { - clean::StrippedItem(..) => {} - clean::RequiredMethodItem(m) => { - assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) - } - clean::MethodItem(m, _) => { - assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) +) -> impl fmt::Display { + fmt::from_fn(move |f| match &item.kind { + clean::StrippedItem(..) => Ok(()), + clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { + assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f) } clean::RequiredAssocConstItem(generics, ty) => assoc_const( - w, item, generics, ty, @@ -1147,9 +1121,9 @@ fn render_assoc_item( link, if parent == ItemType::Trait { 4 } else { 0 }, cx, - ), + ) + .fmt(f), clean::ProvidedAssocConstItem(ci) => assoc_const( - w, item, &ci.generics, &ci.type_, @@ -1157,9 +1131,9 @@ fn render_assoc_item( link, if parent == ItemType::Trait { 4 } else { 0 }, cx, - ), + ) + .fmt(f), clean::ImplAssocConstItem(ci) => assoc_const( - w, item, &ci.generics, &ci.type_, @@ -1167,9 +1141,9 @@ fn render_assoc_item( link, if parent == ItemType::Trait { 4 } else { 0 }, cx, - ), - clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type( - w, + ) + .fmt(f), + clean::RequiredAssocTypeItem(generics, bounds) => assoc_type( item, generics, bounds, @@ -1177,9 +1151,9 @@ fn render_assoc_item( link, if parent == ItemType::Trait { 4 } else { 0 }, cx, - ), - clean::AssocTypeItem(ref ty, ref bounds) => assoc_type( - w, + ) + .fmt(f), + clean::AssocTypeItem(ty, bounds) => assoc_type( item, &ty.generics, bounds, @@ -1187,18 +1161,15 @@ fn render_assoc_item( link, if parent == ItemType::Trait { 4 } else { 0 }, cx, - ), + ) + .fmt(f), _ => panic!("render_assoc_item called on non-associated-item"), - } + }) } // When an attribute is rendered inside a `<pre>` tag, it is formatted using // a whitespace prefix and newline. -fn render_attributes_in_pre<'a, 'tcx: 'a>( - it: &'a clean::Item, - prefix: &'a str, - cx: &'a Context<'tcx>, -) -> impl fmt::Display + Captures<'a> + Captures<'tcx> { +fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display { fmt::from_fn(move |f| { for a in it.attributes(cx.tcx(), cx.cache(), false) { writeln!(f, "{prefix}{a}")?; @@ -1231,28 +1202,28 @@ impl<'a> AssocItemLink<'a> { } pub fn write_section_heading( - w: &mut impl fmt::Write, title: &str, id: &str, extra_class: Option<&str>, extra: impl fmt::Display, -) { - let (extra_class, whitespace) = match extra_class { - Some(extra) => (extra, " "), - None => ("", ""), - }; - write!( - w, - "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\ +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let (extra_class, whitespace) = match extra_class { + Some(extra) => (extra, " "), + None => ("", ""), + }; + write!( + w, + "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\ {title}\ <a href=\"#{id}\" class=\"anchor\">ยง</a>\ </h2>{extra}", - ) - .unwrap(); + ) + }) } -fn write_impl_section_heading(w: &mut impl fmt::Write, title: &str, id: &str) { - write_section_heading(w, title, id, None, "") +fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display { + write_section_heading(title, id, None, "") } pub(crate) fn render_all_impls( @@ -1269,35 +1240,43 @@ pub(crate) fn render_all_impls( buf }; if !impls.is_empty() { - write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations"); - write!(w, "<div id=\"trait-implementations-list\">{impls}</div>").unwrap(); + write!( + w, + "{}<div id=\"trait-implementations-list\">{impls}</div>", + write_impl_section_heading("Trait Implementations", "trait-implementations") + ) + .unwrap(); } if !synthetic.is_empty() { - write_impl_section_heading( - &mut w, - "Auto Trait Implementations", - "synthetic-implementations", - ); - w.write_str("<div id=\"synthetic-implementations-list\">").unwrap(); + write!( + w, + "{}<div id=\"synthetic-implementations-list\">", + write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",) + ) + .unwrap(); render_impls(cx, &mut w, synthetic, containing_item, false); w.write_str("</div>").unwrap(); } if !blanket_impl.is_empty() { - write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations"); - w.write_str("<div id=\"blanket-implementations-list\">").unwrap(); + write!( + w, + "{}<div id=\"blanket-implementations-list\">", + write_impl_section_heading("Blanket Implementations", "blanket-implementations") + ) + .unwrap(); render_impls(cx, &mut w, blanket_impl, containing_item, false); w.write_str("</div>").unwrap(); } } -fn render_assoc_items<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - containing_item: &'a clean::Item, +fn render_assoc_items( + cx: &Context<'_>, + containing_item: &clean::Item, it: DefId, - what: AssocItemRender<'a>, -) -> impl fmt::Display + 'a + Captures<'cx> { + what: AssocItemRender<'_>, +) -> impl fmt::Display { fmt::from_fn(move |f| { let mut derefs = DefIdSet::default(); derefs.insert(it); @@ -1323,25 +1302,34 @@ fn render_assoc_items_inner( let mut tmp_buf = String::new(); let (render_mode, id, class_html) = match what { AssocItemRender::All => { - write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations"); + write_str( + &mut tmp_buf, + format_args!( + "{}", + write_impl_section_heading("Implementations", "implementations") + ), + ); (RenderMode::Normal, "implementations-list".to_owned(), "") } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); let derived_id = cx.derive_id(&id); - tmp_buf.push_str("<details class=\"toggle big-toggle\" open><summary>"); close_tags.push("</details>"); - write_impl_section_heading( + write_str( &mut tmp_buf, - &format!( - "<span>Methods from {trait_}<Target = {type_}></span>", - trait_ = trait_.print(cx), - type_ = type_.print(cx), + format_args!( + "<details class=\"toggle big-toggle\" open><summary>{}</summary>", + write_impl_section_heading( + &format!( + "<span>Methods from {trait_}<Target = {type_}></span>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + ), + &id, + ) ), - &id, ); - tmp_buf.push_str("</summary>"); if let Some(def_id) = type_.def_id(cx.cache()) { cx.deref_id_map.borrow_mut().insert(def_id, id); } @@ -1350,21 +1338,26 @@ fn render_assoc_items_inner( }; let mut impls_buf = String::new(); for i in &non_trait { - render_impl( + write_str( &mut impls_buf, - cx, - i, - containing_item, - AssocItemLink::Anchor(None), - render_mode, - None, - &[], - ImplRenderingParameters { - show_def_docs: true, - show_default_items: true, - show_non_assoc_items: true, - toggle_open_by_default: true, - }, + format_args!( + "{}", + render_impl( + cx, + i, + containing_item, + AssocItemLink::Anchor(None), + render_mode, + None, + &[], + ImplRenderingParameters { + show_def_docs: true, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, + ) + ), ); } if !impls_buf.is_empty() { @@ -1468,10 +1461,10 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> } } -pub(crate) fn notable_traits_button<'a, 'tcx>( - ty: &'a clean::Type, - cx: &'a Context<'tcx>, -) -> Option<impl fmt::Display + 'a + Captures<'tcx>> { +pub(crate) fn notable_traits_button( + ty: &clean::Type, + cx: &Context<'_>, +) -> Option<impl fmt::Display> { let mut has_notable_trait = false; if ty.is_unit() { @@ -1564,20 +1557,23 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { ); for it in &impl_.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { - out.push_str("<div class=\"where\"> "); let empty_set = FxIndexSet::default(); let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); - assoc_type( + write_str( &mut out, - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, + format_args!( + "<div class=\"where\"> {};</div>", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + src_link, + 0, + cx, + ) + ), ); - out.push_str(";</div>"); } } } @@ -1623,7 +1619,6 @@ struct ImplRenderingParameters { } fn render_impl( - w: &mut String, cx: &Context<'_>, i: &Impl, parent: &clean::Item, @@ -1632,513 +1627,549 @@ fn render_impl( use_absolute: Option<bool>, aliases: &[String], rendering_params: ImplRenderingParameters, -) { - let cache = &cx.shared.cache; - let traits = &cache.traits; - let trait_ = i.trait_did().map(|did| &traits[&did]); - let mut close_tags = <Vec<&str>>::with_capacity(2); - - // For trait implementations, the `interesting` output contains all methods that have doc - // comments, and the `boring` output contains all methods that do not. The distinction is - // used to allow hiding the boring methods. - // `containing_item` is used for rendering stability info. If the parent is a trait impl, - // `containing_item` will the grandparent, since trait impls can't have stability attached. - fn doc_impl_item( - boring: &mut String, - interesting: &mut String, - cx: &Context<'_>, - item: &clean::Item, - parent: &clean::Item, - link: AssocItemLink<'_>, - render_mode: RenderMode, - is_default_item: bool, - trait_: Option<&clean::Trait>, - rendering_params: ImplRenderingParameters, - ) { - let item_type = item.type_(); - let name = item.name.as_ref().unwrap(); - - let render_method_item = rendering_params.show_non_assoc_items - && match render_mode { - RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => { - should_render_item(item, deref_mut_, cx.tcx()) - } - }; +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let cache = &cx.shared.cache; + let traits = &cache.traits; + let trait_ = i.trait_did().map(|did| &traits[&did]); + let mut close_tags = <Vec<&str>>::with_capacity(2); + + // For trait implementations, the `interesting` output contains all methods that have doc + // comments, and the `boring` output contains all methods that do not. The distinction is + // used to allow hiding the boring methods. + // `containing_item` is used for rendering stability info. If the parent is a trait impl, + // `containing_item` will the grandparent, since trait impls can't have stability attached. + fn doc_impl_item( + boring: &mut String, + interesting: &mut String, + cx: &Context<'_>, + item: &clean::Item, + parent: &clean::Item, + link: AssocItemLink<'_>, + render_mode: RenderMode, + is_default_item: bool, + trait_: Option<&clean::Trait>, + rendering_params: ImplRenderingParameters, + ) { + let item_type = item.type_(); + let name = item.name.as_ref().unwrap(); + + let render_method_item = rendering_params.show_non_assoc_items + && match render_mode { + RenderMode::Normal => true, + RenderMode::ForDeref { mut_: deref_mut_ } => { + should_render_item(item, deref_mut_, cx.tcx()) + } + }; - let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" }; - - let mut doc_buffer = String::new(); - let mut info_buffer = String::new(); - let mut short_documented = true; - - if render_method_item { - if !is_default_item { - if let Some(t) = trait_ { - // The trait item may have been stripped so we might not - // find any documentation or stability for it. - if let Some(it) = t.items.iter().find(|i| i.name == item.name) { - // We need the stability of the item from the trait - // because impls can't have a stability. - if !item.doc_value().is_empty() { - document_item_info(cx, it, Some(parent)) - .render_into(&mut info_buffer) - .unwrap(); + let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" }; + + let mut doc_buffer = String::new(); + let mut info_buffer = String::new(); + let mut short_documented = true; + + if render_method_item { + if !is_default_item { + if let Some(t) = trait_ { + // The trait item may have been stripped so we might not + // find any documentation or stability for it. + if let Some(it) = t.items.iter().find(|i| i.name == item.name) { + // We need the stability of the item from the trait + // because impls can't have a stability. + if !item.doc_value().is_empty() { + document_item_info(cx, it, Some(parent)) + .render_into(&mut info_buffer) + .unwrap(); + write_str( + &mut doc_buffer, + format_args!("{}", document_full(item, cx, HeadingOffset::H5)), + ); + short_documented = false; + } else { + // In case the item isn't documented, + // provide short documentation from the trait. + write_str( + &mut doc_buffer, + format_args!( + "{}", + document_short( + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ) + ), + ); + } + } + } else { + document_item_info(cx, item, Some(parent)) + .render_into(&mut info_buffer) + .unwrap(); + if rendering_params.show_def_docs { write_str( &mut doc_buffer, format_args!("{}", document_full(item, cx, HeadingOffset::H5)), ); short_documented = false; - } else { - // In case the item isn't documented, - // provide short documentation from the trait. - write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short( - it, - cx, - link, - parent, - rendering_params.show_def_docs, - ) - ), - ); } } } else { - document_item_info(cx, item, Some(parent)) - .render_into(&mut info_buffer) - .unwrap(); - if rendering_params.show_def_docs { - write_str( - &mut doc_buffer, - format_args!("{}", document_full(item, cx, HeadingOffset::H5)), - ); - short_documented = false; - } + write_str( + &mut doc_buffer, + format_args!( + "{}", + document_short(item, cx, link, parent, rendering_params.show_def_docs) + ), + ); } - } else { + } + let w = if short_documented && trait_.is_some() { interesting } else { boring }; + + let toggled = !doc_buffer.is_empty(); + if toggled { + let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short(item, cx, link, parent, rendering_params.show_def_docs) - ), + w, + format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"), ); } - } - let w = if short_documented && trait_.is_some() { interesting } else { boring }; - - let toggled = !doc_buffer.is_empty(); - if toggled { - let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write_str( - w, - format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"), - ); - } - match &item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => { - // Only render when the method is not static or we allow static methods - if render_method_item { - let id = cx.derive_id(format!("{item_type}.{name}")); - let source_id = trait_ - .and_then(|trait_| { - trait_ - .items - .iter() - .find(|item| item.name.map(|n| n == *name).unwrap_or(false)) - }) - .map(|item| format!("{}.{name}", item.type_())); + match &item.kind { + clean::MethodItem(..) | clean::RequiredMethodItem(_) => { + // Only render when the method is not static or we allow static methods + if render_method_item { + let id = cx.derive_id(format!("{item_type}.{name}")); + let source_id = trait_ + .and_then(|trait_| { + trait_ + .items + .iter() + .find(|item| item.name.map(|n| n == *name).unwrap_or(false)) + }) + .map(|item| format!("{}.{name}", item.type_())); + write_str( + w, + format_args!( + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + {}", + render_rightside(cx, item, render_mode) + ), + ); + if trait_.is_some() { + // Anchors are only used on trait impls. + write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + } + write_str( + w, + format_args!( + "<h4 class=\"code-header\">{}</h4></section>", + render_assoc_item( + item, + link.anchor(source_id.as_ref().unwrap_or(&id)), + ItemType::Impl, + cx, + render_mode, + ), + ), + ); + } + } + clean::RequiredAssocConstItem(generics, ty) => { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); write_str( w, - format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"), + format_args!( + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + {}", + render_rightside(cx, item, render_mode) + ), ); - render_rightside(w, cx, item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); } - w.push_str("<h4 class=\"code-header\">"); - render_assoc_item( + write_str( w, - item, - link.anchor(source_id.as_ref().unwrap_or(&id)), - ItemType::Impl, - cx, - render_mode, + format_args!( + "<h4 class=\"code-header\">{}</h4></section>", + assoc_const( + item, + generics, + ty, + AssocConstValue::None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ) + ), ); - w.push_str("</h4></section>"); } - } - clean::RequiredAssocConstItem(ref generics, ref ty) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write_str( - w, - format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"), - ); - render_rightside(w, cx, item, render_mode); - if trait_.is_some() { - // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write_str( + w, + format_args!( + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + {}", + render_rightside(cx, item, render_mode) + ), + ); + if trait_.is_some() { + // Anchors are only used on trait impls. + write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + } + write_str( + w, + format_args!( + "<h4 class=\"code-header\">{}</h4></section>", + assoc_const( + item, + &ci.generics, + &ci.type_, + match item.kind { + clean::ProvidedAssocConstItem(_) => + AssocConstValue::TraitDefault(&ci.kind), + clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), + _ => unreachable!(), + }, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ) + ), + ); } - w.push_str("<h4 class=\"code-header\">"); - assoc_const( - w, - item, - generics, - ty, - AssocConstValue::None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ); - w.push_str("</h4></section>"); - } - clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write_str( - w, - format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"), - ); - render_rightside(w, cx, item, render_mode); - if trait_.is_some() { - // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + clean::RequiredAssocTypeItem(generics, bounds) => { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write_str( + w, + format_args!( + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + {}", + render_rightside(cx, item, render_mode) + ), + ); + if trait_.is_some() { + // Anchors are only used on trait impls. + write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + } + write_str( + w, + format_args!( + "<h4 class=\"code-header\">{}</h4></section>", + assoc_type( + item, + generics, + bounds, + None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ) + ), + ); } - w.push_str("<h4 class=\"code-header\">"); - assoc_const( - w, - item, - &ci.generics, - &ci.type_, - match item.kind { - clean::ProvidedAssocConstItem(_) => AssocConstValue::TraitDefault(&ci.kind), - clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), - _ => unreachable!(), - }, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ); - w.push_str("</h4></section>"); + clean::AssocTypeItem(tydef, _bounds) => { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write_str( + w, + format_args!( + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + {}", + render_rightside(cx, item, render_mode) + ), + ); + if trait_.is_some() { + // Anchors are only used on trait impls. + write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + } + write_str( + w, + format_args!( + "<h4 class=\"code-header\">{}</h4></section>", + assoc_type( + item, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, + ) + ), + ); + } + clean::StrippedItem(..) => return, + _ => panic!("can't make docs for trait item with name {:?}", item.name), } - clean::RequiredAssocTypeItem(ref generics, ref bounds) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write_str( - w, - format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"), - ); - render_rightside(w, cx, item, render_mode); - if trait_.is_some() { - // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); + + w.push_str(&info_buffer); + if toggled { + w.push_str("</summary>"); + w.push_str(&doc_buffer); + w.push_str("</details>"); + } + } + + let mut impl_items = String::new(); + let mut default_impl_items = String::new(); + let impl_ = i.inner_impl(); + + // Impl items are grouped by kinds: + // + // 1. Constants + // 2. Types + // 3. Functions + // + // This order is because you can have associated constants used in associated types (like array + // length), and both in associcated functions. So with this order, when reading from top to + // bottom, you should see items definitions before they're actually used most of the time. + let mut assoc_types = Vec::new(); + let mut methods = Vec::new(); + + if !impl_.is_negative_trait_impl() { + for trait_item in &impl_.items { + match trait_item.kind { + clean::MethodItem(..) | clean::RequiredMethodItem(_) => { + methods.push(trait_item) + } + clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { + assoc_types.push(trait_item) + } + clean::RequiredAssocConstItem(..) + | clean::ProvidedAssocConstItem(_) + | clean::ImplAssocConstItem(_) => { + // We render it directly since they're supposed to come first. + doc_impl_item( + &mut default_impl_items, + &mut impl_items, + cx, + trait_item, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, + ); + } + _ => {} } - w.push_str("<h4 class=\"code-header\">"); - assoc_type( - w, - item, - generics, - bounds, - None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, + } + + for assoc_type in assoc_types { + doc_impl_item( + &mut default_impl_items, + &mut impl_items, cx, + assoc_type, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, ); - w.push_str("</h4></section>"); } - clean::AssocTypeItem(tydef, _bounds) => { - let source_id = format!("{item_type}.{name}"); - let id = cx.derive_id(&source_id); - write_str( - w, - format_args!("<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"), - ); - render_rightside(w, cx, item, render_mode); - if trait_.is_some() { - // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">ยง</a>")); - } - w.push_str("<h4 class=\"code-header\">"); - assoc_type( - w, - item, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, + for method in methods { + doc_impl_item( + &mut default_impl_items, + &mut impl_items, cx, + method, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, ); - w.push_str("</h4></section>"); } - clean::StrippedItem(..) => return, - _ => panic!("can't make docs for trait item with name {:?}", item.name), } - w.push_str(&info_buffer); - if toggled { - w.push_str("</summary>"); - w.push_str(&doc_buffer); - w.push_str("</details>"); - } - } - - let mut impl_items = String::new(); - let mut default_impl_items = String::new(); - let impl_ = i.inner_impl(); - - // Impl items are grouped by kinds: - // - // 1. Constants - // 2. Types - // 3. Functions - // - // This order is because you can have associated constants used in associated types (like array - // length), and both in associcated functions. So with this order, when reading from top to - // bottom, you should see items definitions before they're actually used most of the time. - let mut assoc_types = Vec::new(); - let mut methods = Vec::new(); - - if !impl_.is_negative_trait_impl() { - for trait_item in &impl_.items { - match trait_item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(trait_item), - clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { - assoc_types.push(trait_item) + fn render_default_items( + boring: &mut String, + interesting: &mut String, + cx: &Context<'_>, + t: &clean::Trait, + i: &clean::Impl, + parent: &clean::Item, + render_mode: RenderMode, + rendering_params: ImplRenderingParameters, + ) { + for trait_item in &t.items { + // Skip over any default trait items that are impossible to reference + // (e.g. if it has a `Self: Sized` bound on an unsized type). + if let Some(impl_def_id) = parent.item_id.as_def_id() + && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() + && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id)) + { + continue; } - clean::RequiredAssocConstItem(..) - | clean::ProvidedAssocConstItem(_) - | clean::ImplAssocConstItem(_) => { - // We render it directly since they're supposed to come first. - doc_impl_item( - &mut default_impl_items, - &mut impl_items, - cx, - trait_item, - if trait_.is_some() { &i.impl_item } else { parent }, - link, - render_mode, - false, - trait_, - rendering_params, - ); + + let n = trait_item.name; + if i.items.iter().any(|m| m.name == n) { + continue; } - _ => {} - } - } + let did = i.trait_.as_ref().unwrap().def_id(); + let provided_methods = i.provided_trait_methods(cx.tcx()); + let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); - for assoc_type in assoc_types { - doc_impl_item( - &mut default_impl_items, - &mut impl_items, - cx, - assoc_type, - if trait_.is_some() { &i.impl_item } else { parent }, - link, - render_mode, - false, - trait_, - rendering_params, - ); - } - for method in methods { - doc_impl_item( - &mut default_impl_items, - &mut impl_items, - cx, - method, - if trait_.is_some() { &i.impl_item } else { parent }, - link, - render_mode, - false, - trait_, - rendering_params, - ); + doc_impl_item( + boring, + interesting, + cx, + trait_item, + parent, + assoc_link, + render_mode, + true, + Some(t), + rendering_params, + ); + } } - } - fn render_default_items( - boring: &mut String, - interesting: &mut String, - cx: &Context<'_>, - t: &clean::Trait, - i: &clean::Impl, - parent: &clean::Item, - render_mode: RenderMode, - rendering_params: ImplRenderingParameters, - ) { - for trait_item in &t.items { - // Skip over any default trait items that are impossible to reference - // (e.g. if it has a `Self: Sized` bound on an unsized type). - if let Some(impl_def_id) = parent.item_id.as_def_id() - && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() - && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id)) + // If we've implemented a trait, then also emit documentation for all + // default items which weren't overridden in the implementation block. + // We don't emit documentation for default items if they appear in the + // Implementations on Foreign Types or Implementors sections. + if rendering_params.show_default_items { + if let Some(t) = trait_ + && !impl_.is_negative_trait_impl() { - continue; - } - - let n = trait_item.name; - if i.items.iter().any(|m| m.name == n) { - continue; + render_default_items( + &mut default_impl_items, + &mut impl_items, + cx, + t, + impl_, + &i.impl_item, + render_mode, + rendering_params, + ); } - let did = i.trait_.as_ref().unwrap().def_id(); - let provided_methods = i.provided_trait_methods(cx.tcx()); - let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); - - doc_impl_item( - boring, - interesting, - cx, - trait_item, - parent, - assoc_link, - render_mode, - true, - Some(t), - rendering_params, - ); - } - } - - // If we've implemented a trait, then also emit documentation for all - // default items which weren't overridden in the implementation block. - // We don't emit documentation for default items if they appear in the - // Implementations on Foreign Types or Implementors sections. - if rendering_params.show_default_items { - if let Some(t) = trait_ - && !impl_.is_negative_trait_impl() - { - render_default_items( - &mut default_impl_items, - &mut impl_items, - cx, - t, - impl_, - &i.impl_item, - render_mode, - rendering_params, - ); } - } - if render_mode == RenderMode::Normal { - let toggled = !(impl_items.is_empty() && default_impl_items.is_empty()); - if toggled { - close_tags.push("</details>"); - write_str( - w, - format_args!( + if render_mode == RenderMode::Normal { + let toggled = !(impl_items.is_empty() && default_impl_items.is_empty()); + if toggled { + close_tags.push("</details>"); + write!( + w, "<details class=\"toggle implementors-toggle\"{}>\ <summary>", if rendering_params.toggle_open_by_default { " open" } else { "" } - ), - ); - } + )?; + } - let (before_dox, after_dox) = i - .impl_item - .opt_doc_value() - .map(|dox| { - Markdown { - content: &dox, - links: &i.impl_item.links(cx), - ids: &mut cx.id_map.borrow_mut(), - error_codes: cx.shared.codes, - edition: cx.shared.edition(), - playground: &cx.shared.playground, - heading_offset: HeadingOffset::H4, - } - .split_summary_and_content() - }) - .unwrap_or((None, None)); - render_impl_summary( - w, - cx, - i, - parent, - rendering_params.show_def_docs, - use_absolute, - aliases, - &before_dox, - ); - if toggled { - w.push_str("</summary>"); - } + let (before_dox, after_dox) = i + .impl_item + .opt_doc_value() + .map(|dox| { + Markdown { + content: &dox, + links: &i.impl_item.links(cx), + ids: &mut cx.id_map.borrow_mut(), + error_codes: cx.shared.codes, + edition: cx.shared.edition(), + playground: &cx.shared.playground, + heading_offset: HeadingOffset::H4, + } + .split_summary_and_content() + }) + .unwrap_or((None, None)); + write!( + w, + "{}", + render_impl_summary( + cx, + i, + parent, + rendering_params.show_def_docs, + use_absolute, + aliases, + before_dox.as_deref(), + ) + )?; + if toggled { + w.write_str("</summary>")?; + } - if before_dox.is_some() { - if trait_.is_none() && impl_.items.is_empty() { - w.push_str( - "<div class=\"item-info\">\ + if before_dox.is_some() { + if trait_.is_none() && impl_.items.is_empty() { + w.write_str( + "<div class=\"item-info\">\ <div class=\"stab empty-impl\">This impl block contains no items.</div>\ </div>", - ); + )?; + } + if let Some(after_dox) = after_dox { + write!(w, "<div class=\"docblock\">{after_dox}</div>")?; + } } - if let Some(after_dox) = after_dox { - write_str(w, format_args!("<div class=\"docblock\">{after_dox}</div>")); + if !default_impl_items.is_empty() || !impl_items.is_empty() { + w.write_str("<div class=\"impl-items\">")?; + close_tags.push("</div>"); } } if !default_impl_items.is_empty() || !impl_items.is_empty() { - w.push_str("<div class=\"impl-items\">"); - close_tags.push("</div>"); + w.write_str(&default_impl_items)?; + w.write_str(&impl_items)?; } - } - if !default_impl_items.is_empty() || !impl_items.is_empty() { - w.push_str(&default_impl_items); - w.push_str(&impl_items); - } - for tag in close_tags.into_iter().rev() { - w.push_str(tag); - } + for tag in close_tags.into_iter().rev() { + w.write_str(tag)?; + } + Ok(()) + }) } // Render the items that appear on the right side of methods, impls, and // associated types. For example "1.0.0 (const: 1.39.0) ยท source". -fn render_rightside(w: &mut String, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) { +fn render_rightside( + cx: &Context<'_>, + item: &clean::Item, + render_mode: RenderMode, +) -> impl fmt::Display { let tcx = cx.tcx(); - // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove - // this condition. - let const_stability = match render_mode { - RenderMode::Normal => item.const_stability(tcx), - RenderMode::ForDeref { .. } => None, - }; - let src_href = cx.src_href(item); - let has_src_ref = src_href.is_some(); - - let mut rightside = String::new(); - let has_stability = render_stability_since_raw_with_extra( - &mut rightside, - item.stable_since(tcx), - const_stability, - if has_src_ref { "" } else { " rightside" }, - ); - if let Some(link) = src_href { - if has_stability { - write_str( - &mut rightside, - format_args!(" ยท <a class=\"src\" href=\"{link}\">Source</a>"), - ); - } else { - write_str( - &mut rightside, - format_args!("<a class=\"src rightside\" href=\"{link}\">Source</a>"), - ); + fmt::from_fn(move |w| { + // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove + // this condition. + let const_stability = match render_mode { + RenderMode::Normal => item.const_stability(tcx), + RenderMode::ForDeref { .. } => None, + }; + let src_href = cx.src_href(item); + let stability = render_stability_since_raw_with_extra( + item.stable_since(tcx), + const_stability, + if src_href.is_some() { "" } else { " rightside" }, + ); + + match (stability, src_href) { + (Some(stability), Some(link)) => { + write!( + w, + "<span class=\"rightside\">{stability} ยท <a class=\"src\" href=\"{link}\">Source</a></span>", + ) + } + (Some(stability), None) => { + write!(w, "{stability}") + } + (None, Some(link)) => { + write!(w, "<a class=\"src rightside\" href=\"{link}\">Source</a>") + } + (None, None) => Ok(()), } - } - if has_stability && has_src_ref { - write_str(w, format_args!("<span class=\"rightside\">{rightside}</span>")); - } else { - w.push_str(&rightside); - } + }) } pub(crate) fn render_impl_summary( - w: &mut String, cx: &Context<'_>, i: &Impl, parent: &clean::Item, @@ -2147,67 +2178,67 @@ pub(crate) fn render_impl_summary( // This argument is used to reference same type with different paths to avoid duplication // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], - doc: &Option<String>, -) { - let inner_impl = i.inner_impl(); - let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id)); - let aliases = (!aliases.is_empty()) - .then_some(fmt::from_fn(|f| { - write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f))) - })) - .maybe_display(); - write_str(w, format_args!("<section id=\"{id}\" class=\"impl\"{aliases}>")); - render_rightside(w, cx, &i.impl_item, RenderMode::Normal); - write_str( - w, - format_args!( - "<a href=\"#{id}\" class=\"anchor\">ยง</a>\ - <h3 class=\"code-header\">" - ), - ); - - if let Some(use_absolute) = use_absolute { - write_str(w, format_args!("{}", inner_impl.print(use_absolute, cx))); - if show_def_docs { - for it in &inner_impl.items { - if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { - w.push_str("<div class=\"where\"> "); - assoc_type( - w, - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - AssocItemLink::Anchor(None), - 0, - cx, - ); - w.push_str(";</div>"); + doc: Option<&str>, +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let inner_impl = i.inner_impl(); + let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id)); + let aliases = (!aliases.is_empty()) + .then_some(fmt::from_fn(|f| { + write!(f, " data-aliases=\"{}\"", fmt::from_fn(|f| aliases.iter().joined(",", f))) + })) + .maybe_display(); + write!( + w, + "<section id=\"{id}\" class=\"impl\"{aliases}>\ + {}\ + <a href=\"#{id}\" class=\"anchor\">ยง</a>\ + <h3 class=\"code-header\">", + render_rightside(cx, &i.impl_item, RenderMode::Normal) + )?; + + if let Some(use_absolute) = use_absolute { + write!(w, "{}", inner_impl.print(use_absolute, cx))?; + if show_def_docs { + for it in &inner_impl.items { + if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { + write!( + w, + "<div class=\"where\"> {};</div>", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + AssocItemLink::Anchor(None), + 0, + cx, + ) + )?; + } } } + } else { + write!(w, "{}", inner_impl.print(false, cx))?; } - } else { - write_str(w, format_args!("{}", inner_impl.print(false, cx))); - } - w.push_str("</h3>"); + w.write_str("</h3>")?; - let is_trait = inner_impl.trait_.is_some(); - if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) { - write_str( - w, - format_args!( + let is_trait = inner_impl.trait_.is_some(); + if is_trait && let Some(portability) = portability(&i.impl_item, Some(parent)) { + write!( + w, "<span class=\"item-info\">\ - <div class=\"stab portability\">{portability}</div>\ - </span>", - ), - ); - } + <div class=\"stab portability\">{portability}</div>\ + </span>", + )?; + } - if let Some(doc) = doc { - write_str(w, format_args!("<div class=\"docblock\">{doc}</div>")); - } + if let Some(doc) = doc { + write!(w, "<div class=\"docblock\">{doc}</div>")?; + } - w.push_str("</section>"); + w.write_str("</section>") + }) } pub(crate) fn small_url_encode(s: String) -> String { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f3201147039..c599a84ee44 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,10 +1,9 @@ use std::cmp::Ordering; use std::fmt; -use std::fmt::Display; +use std::fmt::{Display, Write as _}; use rinja::Template; use rustc_abi::VariantIdx; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -32,7 +31,7 @@ use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyTextWithWbr}; use crate::html::format::{ Ending, PrintWithSpace, join_with_double_colon, print_abi_with_space, - print_constness_with_space, print_where_clause, visibility_print_with_space, write_str, + print_constness_with_space, print_where_clause, visibility_print_with_space, }; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; @@ -92,44 +91,32 @@ macro_rules! item_template { macro_rules! item_template_methods { () => {}; (document $($rest:tt)*) => { - fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let v = document(cx, item, None, HeadingOffset::H2); - write!(f, "{v}") - }) + fn document(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + document(cx, item, None, HeadingOffset::H2) } item_template_methods!($($rest)*); }; (document_type_layout $($rest:tt)*) => { - fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let def_id = item.item_id.expect_def_id(); - let v = document_type_layout(cx, def_id); - write!(f, "{v}") - }) + fn document_type_layout(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + let def_id = item.item_id.expect_def_id(); + document_type_layout(cx, def_id) } 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> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let v = render_attributes_in_pre(item, "", cx); - write!(f, "{v}") - }) + fn render_attributes_in_pre(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + render_attributes_in_pre(item, "", cx) } item_template_methods!($($rest)*); }; (render_assoc_items $($rest:tt)*) => { - fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_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 render_assoc_items(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + let def_id = item.item_id.expect_def_id(); + render_assoc_items(cx, item, def_id, AssocItemRender::All) } item_template_methods!($($rest)*); }; @@ -162,132 +149,136 @@ struct ItemVars<'a> { src_href: Option<&'a str>, } -/// Calls `print_where_clause` and returns `true` if a `where` clause was generated. -fn print_where_clause_and_check<'a, 'tcx: 'a>( - buffer: &mut String, - gens: &'a clean::Generics, - cx: &'a Context<'tcx>, -) -> bool { - let len_before = buffer.len(); - write_str(buffer, format_args!("{}", print_where_clause(gens, cx, 0, Ending::Newline))); - len_before != buffer.len() -} - -pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut String) { +pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Display { debug_assert!(!item.is_stripped()); - let typ = match item.kind { - clean::ModuleItem(_) => { - if item.is_crate() { - "Crate " - } else { - "Module " - } - } - clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", - clean::TraitItem(..) => "Trait ", - clean::StructItem(..) => "Struct ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypeAliasItem(..) => "Type Alias ", - clean::MacroItem(..) => "Macro ", - clean::ProcMacroItem(ref mac) => match mac.kind { - MacroKind::Bang => "Macro ", - MacroKind::Attr => "Attribute Macro ", - MacroKind::Derive => "Derive Macro ", - }, - clean::PrimitiveItem(..) => "Primitive Type ", - clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", - clean::ConstantItem(..) => "Constant ", - clean::ForeignTypeItem => "Foreign Type ", - clean::KeywordItem => "Keyword ", - clean::TraitAliasItem(..) => "Trait Alias ", - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - }; - let stability_since_raw = { - let mut buf = String::new(); - render_stability_since_raw( - &mut buf, - item.stable_since(cx.tcx()), - item.const_stability(cx.tcx()), - ); - buf - }; - // Write source tag - // - // When this item is part of a `crate use` in a downstream crate, the - // source link in the downstream documentation will actually come back to - // this page, and this link will be auto-clicked. The `id` attribute is - // used to find the link to auto-click. - let src_href = - if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None }; - - let path_components = if item.is_primitive() || item.is_keyword() { - vec![] - } else { - let cur = &cx.current; - let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; - cur.iter() - .enumerate() - .take(amt) - .map(|(i, component)| PathComponent { - path: "../".repeat(cur.len() - i - 1), - name: *component, - }) - .collect() - }; + fmt::from_fn(|buf| { + let typ = match item.kind { + clean::ModuleItem(_) => { + if item.is_crate() { + "Crate " + } else { + "Module " + } + } + clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", + clean::TraitItem(..) => "Trait ", + clean::StructItem(..) => "Struct ", + clean::UnionItem(..) => "Union ", + clean::EnumItem(..) => "Enum ", + clean::TypeAliasItem(..) => "Type Alias ", + clean::MacroItem(..) => "Macro ", + clean::ProcMacroItem(ref mac) => match mac.kind { + MacroKind::Bang => "Macro ", + MacroKind::Attr => "Attribute Macro ", + MacroKind::Derive => "Derive Macro ", + }, + clean::PrimitiveItem(..) => "Primitive Type ", + clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", + clean::ConstantItem(..) => "Constant ", + clean::ForeignTypeItem => "Foreign Type ", + clean::KeywordItem => "Keyword ", + clean::TraitAliasItem(..) => "Trait Alias ", + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + }; + let stability_since_raw = + render_stability_since_raw(item.stable_since(cx.tcx()), item.const_stability(cx.tcx())) + .maybe_display() + .to_string(); + + // Write source tag + // + // When this item is part of a `crate use` in a downstream crate, the + // source link in the downstream documentation will actually come back to + // this page, and this link will be auto-clicked. The `id` attribute is + // used to find the link to auto-click. + let src_href = + if cx.info.include_sources && !item.is_primitive() { cx.src_href(item) } else { None }; + + let path_components = if item.is_primitive() || item.is_keyword() { + vec![] + } else { + let cur = &cx.current; + let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; + cur.iter() + .enumerate() + .take(amt) + .map(|(i, component)| PathComponent { + path: "../".repeat(cur.len() - i - 1), + name: *component, + }) + .collect() + }; - let item_vars = ItemVars { - typ, - name: item.name.as_ref().unwrap().as_str(), - item_type: &item.type_().to_string(), - path_components, - stability_since_raw: &stability_since_raw, - src_href: src_href.as_deref(), - }; + let item_vars = ItemVars { + typ, + name: item.name.as_ref().unwrap().as_str(), + item_type: &item.type_().to_string(), + path_components, + stability_since_raw: &stability_since_raw, + src_href: src_href.as_deref(), + }; - item_vars.render_into(buf).unwrap(); + item_vars.render_into(buf).unwrap(); - match &item.kind { - clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => { - item_function(buf, cx, item, f) - } - clean::TraitItem(ref t) => item_trait(buf, cx, item, t), - clean::StructItem(ref s) => item_struct(buf, cx, item, s), - clean::UnionItem(ref s) => item_union(buf, cx, item, s), - clean::EnumItem(ref e) => item_enum(buf, cx, item, e), - clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t), - clean::MacroItem(ref m) => item_macro(buf, cx, item, m), - clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), - clean::PrimitiveItem(_) => item_primitive(buf, cx, item), - clean::StaticItem(ref i) => item_static(buf, cx, item, i, None), - clean::ForeignStaticItem(ref i, safety) => item_static(buf, cx, item, i, Some(*safety)), - clean::ConstantItem(ci) => item_constant(buf, cx, item, &ci.generics, &ci.type_, &ci.kind), - clean::ForeignTypeItem => item_foreign_type(buf, cx, item), - clean::KeywordItem => item_keyword(buf, cx, item), - clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - } + match &item.kind { + clean::ModuleItem(m) => { + write!(buf, "{}", item_module(cx, item, &m.items)) + } + clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => { + write!(buf, "{}", item_function(cx, item, f)) + } + clean::TraitItem(t) => write!(buf, "{}", item_trait(cx, item, t)), + clean::StructItem(s) => { + write!(buf, "{}", item_struct(cx, item, s)) + } + clean::UnionItem(s) => write!(buf, "{}", item_union(cx, item, s)), + clean::EnumItem(e) => write!(buf, "{}", item_enum(cx, item, e)), + clean::TypeAliasItem(t) => { + write!(buf, "{}", item_type_alias(cx, item, t)) + } + clean::MacroItem(m) => write!(buf, "{}", item_macro(cx, item, m)), + clean::ProcMacroItem(m) => { + write!(buf, "{}", item_proc_macro(cx, item, m)) + } + clean::PrimitiveItem(_) => write!(buf, "{}", item_primitive(cx, item)), + clean::StaticItem(i) => { + write!(buf, "{}", item_static(cx, item, i, None)) + } + clean::ForeignStaticItem(i, safety) => { + write!(buf, "{}", item_static(cx, item, i, Some(*safety))) + } + clean::ConstantItem(ci) => { + write!(buf, "{}", item_constant(cx, item, &ci.generics, &ci.type_, &ci.kind)) + } + clean::ForeignTypeItem => { + write!(buf, "{}", item_foreign_type(cx, item)) + } + clean::KeywordItem => write!(buf, "{}", item_keyword(cx, item)), + clean::TraitAliasItem(ta) => { + write!(buf, "{}", item_trait_alias(cx, item, ta)) + } + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + }?; - // Render notable-traits.js used for all methods in this module. - let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut(); - if !types_with_notable_traits.is_empty() { - write_str( - buf, - format_args!( + // Render notable-traits.js used for all methods in this module. + let mut types_with_notable_traits = cx.types_with_notable_traits.borrow_mut(); + if !types_with_notable_traits.is_empty() { + write!( + buf, r#"<script type="text/json" id="notable-traits-data">{}</script>"#, - notable_traits_json(types_with_notable_traits.iter(), cx) - ), - ); - types_with_notable_traits.clear(); - } + notable_traits_json(types_with_notable_traits.iter(), cx), + )?; + types_with_notable_traits.clear(); + } + Ok(()) + }) } /// For large structs, enums, unions, etc, determine whether to hide their fields @@ -314,192 +305,194 @@ trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display { fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>); } -fn item_module(w: &mut String, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) { - write_str(w, format_args!("{}", document(cx, item, None, HeadingOffset::H2))); - - let mut not_stripped_items = - items.iter().filter(|i| !i.is_stripped()).enumerate().collect::<Vec<_>>(); - - // the order of item types in the listing - fn reorder(ty: ItemType) -> u8 { - match ty { - ItemType::ExternCrate => 0, - ItemType::Import => 1, - ItemType::Primitive => 2, - ItemType::Module => 3, - ItemType::Macro => 4, - ItemType::Struct => 5, - ItemType::Enum => 6, - ItemType::Constant => 7, - ItemType::Static => 8, - ItemType::Trait => 9, - ItemType::Function => 10, - ItemType::TypeAlias => 12, - ItemType::Union => 13, - _ => 14 + ty as u8, +fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display { + fmt::from_fn(|w| { + write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?; + + let mut not_stripped_items = + items.iter().filter(|i| !i.is_stripped()).enumerate().collect::<Vec<_>>(); + + // the order of item types in the listing + fn reorder(ty: ItemType) -> u8 { + match ty { + ItemType::ExternCrate => 0, + ItemType::Import => 1, + ItemType::Primitive => 2, + ItemType::Module => 3, + ItemType::Macro => 4, + ItemType::Struct => 5, + ItemType::Enum => 6, + ItemType::Constant => 7, + ItemType::Static => 8, + ItemType::Trait => 9, + ItemType::Function => 10, + ItemType::TypeAlias => 12, + ItemType::Union => 13, + _ => 14 + ty as u8, + } } - } - fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering { - let rty1 = reorder(i1.type_()); - let rty2 = reorder(i2.type_()); - if rty1 != rty2 { - return rty1.cmp(&rty2); - } - let is_stable1 = i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true); - let is_stable2 = i2.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true); - if is_stable1 != is_stable2 { - // true is bigger than false in the standard bool ordering, - // but we actually want stable items to come first - return is_stable2.cmp(&is_stable1); + fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering { + let rty1 = reorder(i1.type_()); + let rty2 = reorder(i2.type_()); + if rty1 != rty2 { + return rty1.cmp(&rty2); + } + let is_stable1 = + i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true); + let is_stable2 = + i2.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true); + if is_stable1 != is_stable2 { + // true is bigger than false in the standard bool ordering, + // but we actually want stable items to come first + return is_stable2.cmp(&is_stable1); + } + let lhs = i1.name.unwrap_or(kw::Empty); + let rhs = i2.name.unwrap_or(kw::Empty); + compare_names(lhs.as_str(), rhs.as_str()) } - let lhs = i1.name.unwrap_or(kw::Empty); - let rhs = i2.name.unwrap_or(kw::Empty); - compare_names(lhs.as_str(), rhs.as_str()) - } - let tcx = cx.tcx(); + let tcx = cx.tcx(); - match cx.shared.module_sorting { - ModuleSorting::Alphabetical => { - not_stripped_items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx)); - } - ModuleSorting::DeclarationOrder => {} - } - // This call is to remove re-export duplicates in cases such as: - // - // ``` - // pub(crate) mod foo { - // pub(crate) mod bar { - // pub(crate) trait Double { fn foo(); } - // } - // } - // - // pub(crate) use foo::bar::*; - // pub(crate) use foo::*; - // ``` - // - // `Double` will appear twice in the generated docs. - // - // FIXME: This code is quite ugly and could be improved. Small issue: DefId - // can be identical even if the elements are different (mostly in imports). - // So in case this is an import, we keep everything by adding a "unique id" - // (which is the position in the vector). - not_stripped_items.dedup_by_key(|(idx, i)| { - ( - i.item_id, - if i.name.is_some() { Some(full_path(cx, i)) } else { None }, - i.type_(), - if i.is_import() { *idx } else { 0 }, - ) - }); + match cx.shared.module_sorting { + ModuleSorting::Alphabetical => { + not_stripped_items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx)); + } + ModuleSorting::DeclarationOrder => {} + } + // This call is to remove re-export duplicates in cases such as: + // + // ``` + // pub(crate) mod foo { + // pub(crate) mod bar { + // pub(crate) trait Double { fn foo(); } + // } + // } + // + // pub(crate) use foo::bar::*; + // pub(crate) use foo::*; + // ``` + // + // `Double` will appear twice in the generated docs. + // + // FIXME: This code is quite ugly and could be improved. Small issue: DefId + // can be identical even if the elements are different (mostly in imports). + // So in case this is an import, we keep everything by adding a "unique id" + // (which is the position in the vector). + not_stripped_items.dedup_by_key(|(idx, i)| { + ( + i.item_id, + if i.name.is_some() { Some(full_path(cx, i)) } else { None }, + i.type_(), + if i.is_import() { *idx } else { 0 }, + ) + }); - debug!("{not_stripped_items:?}"); - let mut last_section = None; + debug!("{not_stripped_items:?}"); + let mut last_section = None; - for (_, myitem) in ¬_stripped_items { - let my_section = item_ty_to_section(myitem.type_()); - if Some(my_section) != last_section { - if last_section.is_some() { - w.push_str(ITEM_TABLE_CLOSE); + for (_, myitem) in ¬_stripped_items { + let my_section = item_ty_to_section(myitem.type_()); + if Some(my_section) != last_section { + if last_section.is_some() { + w.write_str(ITEM_TABLE_CLOSE)?; + } + last_section = Some(my_section); + let section_id = my_section.id(); + let tag = + if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN }; + write!( + w, + "{}", + write_section_heading(my_section.name(), &cx.derive_id(section_id), None, tag) + )?; } - last_section = Some(my_section); - let section_id = my_section.id(); - let tag = - if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN }; - write_section_heading(w, my_section.name(), &cx.derive_id(section_id), None, tag); - } - match myitem.kind { - clean::ExternCrateItem { ref src } => { - use crate::html::format::anchor; + match myitem.kind { + clean::ExternCrateItem { ref src } => { + use crate::html::format::anchor; - match *src { - Some(src) => { - write_str( - w, - format_args!( + match *src { + Some(src) => { + write!( + w, "<dt><code>{}extern crate {} as {};", visibility_print_with_space(myitem, cx), anchor(myitem.item_id.expect_def_id(), src, cx), EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()) - ), - ); - } - None => { - write_str( - w, - format_args!( + )?; + } + None => { + write!( + w, "<dt><code>{}extern crate {};", visibility_print_with_space(myitem, cx), anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx) - ), - ); + )?; + } } + w.write_str("</code></dt>")?; } - w.push_str("</code></dt>"); - } - clean::ImportItem(ref import) => { - let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| { - extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string() - }); + clean::ImportItem(ref import) => { + let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| { + extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string() + }); - let id = match import.kind { - clean::ImportKind::Simple(s) => { - format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) - } - clean::ImportKind::Glob => String::new(), - }; - write_str( - w, - format_args!( + let id = match import.kind { + clean::ImportKind::Simple(s) => { + format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) + } + clean::ImportKind::Glob => String::new(), + }; + write!( + w, "<dt{id}>\ <code>{vis}{imp}</code>{stab_tags}\ </dt>", vis = visibility_print_with_space(myitem, cx), imp = import.print(cx) - ), - ); - } - - _ => { - if myitem.name.is_none() { - continue; + )?; } - let unsafety_flag = match myitem.kind { - clean::FunctionItem(_) | clean::ForeignFunctionItem(..) - if myitem.fn_header(tcx).unwrap().is_unsafe() => - { - "<sup title=\"unsafe function\">โ </sup>" + _ => { + if myitem.name.is_none() { + continue; } - clean::ForeignStaticItem(_, hir::Safety::Unsafe) => { - "<sup title=\"unsafe static\">โ </sup>" - } - _ => "", - }; - let visibility_and_hidden = match myitem.visibility(tcx) { - Some(ty::Visibility::Restricted(_)) => { - if myitem.is_doc_hidden() { - // Don't separate with a space when there are two of them - "<span title=\"Restricted Visibility\"> ๐</span><span title=\"Hidden item\">๐ป</span> " - } else { - "<span title=\"Restricted Visibility\"> ๐</span> " + let unsafety_flag = match myitem.kind { + clean::FunctionItem(_) | clean::ForeignFunctionItem(..) + if myitem.fn_header(tcx).unwrap().is_unsafe() => + { + "<sup title=\"unsafe function\">โ </sup>" } - } - _ if myitem.is_doc_hidden() => "<span title=\"Hidden item\"> ๐ป</span> ", - _ => "", - }; - - let docs = - MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string(); - let (docs_before, docs_after) = - if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") }; - write_str( - w, - format_args!( + clean::ForeignStaticItem(_, hir::Safety::Unsafe) => { + "<sup title=\"unsafe static\">โ </sup>" + } + _ => "", + }; + + let visibility_and_hidden = match myitem.visibility(tcx) { + Some(ty::Visibility::Restricted(_)) => { + if myitem.is_doc_hidden() { + // Don't separate with a space when there are two of them + "<span title=\"Restricted Visibility\"> ๐</span><span title=\"Hidden item\">๐ป</span> " + } else { + "<span title=\"Restricted Visibility\"> ๐</span> " + } + } + _ if myitem.is_doc_hidden() => { + "<span title=\"Hidden item\"> ๐ป</span> " + } + _ => "", + }; + + let docs = + MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string(); + let (docs_before, docs_after) = + if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") }; + write!( + w, "<dt>\ <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\ {visibility_and_hidden}\ @@ -514,27 +507,28 @@ fn item_module(w: &mut String, cx: &Context<'_>, item: &clean::Item, items: &[cl unsafety_flag = unsafety_flag, href = item_path(myitem.type_(), myitem.name.unwrap().as_str()), title = format_args!("{} {}", myitem.type_(), full_path(cx, myitem)), - ), - ); + )?; + } } } - } - if last_section.is_some() { - w.push_str(ITEM_TABLE_CLOSE); - } + if last_section.is_some() { + w.write_str(ITEM_TABLE_CLOSE)?; + } + Ok(()) + }) } /// Render the stability, deprecation and portability tags that are displayed in the item's summary /// at the module level. -fn extra_info_tags<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - item: &'a clean::Item, - parent: &'a clean::Item, +fn extra_info_tags( + tcx: TyCtxt<'_>, + item: &clean::Item, + parent: &clean::Item, import_def_id: Option<DefId>, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { - fn tag_html<'a>(class: &'a str, title: &'a str, contents: &'a str) -> impl Display + 'a { + fn tag_html(class: &str, title: &str, contents: &str) -> impl Display { fmt::from_fn(move |f| { write!( f, @@ -583,44 +577,43 @@ fn extra_info_tags<'a, 'tcx: 'a>( }) } -fn item_function(w: &mut String, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { - let tcx = cx.tcx(); - let header = it.fn_header(tcx).expect("printing a function which isn't a function"); - debug!( - "item_function/const: {:?} {:?} {:?} {:?}", - it.name, - &header.constness, - it.stable_since(tcx), - it.const_stability(tcx), - ); - let constness = print_constness_with_space( - &header.constness, - it.stable_since(tcx), - it.const_stability(tcx), - ); - let safety = header.safety.print_with_space(); - let abi = print_abi_with_space(header.abi).to_string(); - let asyncness = header.asyncness.print_with_space(); - let visibility = visibility_print_with_space(it, cx).to_string(); - let name = it.name.unwrap(); - - let generics_len = format!("{:#}", f.generics.print(cx)).len(); - let header_len = "fn ".len() - + visibility.len() - + constness.len() - + asyncness.len() - + safety.len() - + abi.len() - + name.as_str().len() - + generics_len; - - let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display(); - - wrap_item(w, |w| { - w.reserve(header_len); - write_str( - w, - format_args!( +fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> impl fmt::Display { + fmt::from_fn(|w| { + let tcx = cx.tcx(); + let header = it.fn_header(tcx).expect("printing a function which isn't a function"); + debug!( + "item_function/const: {:?} {:?} {:?} {:?}", + it.name, + &header.constness, + it.stable_since(tcx), + it.const_stability(tcx), + ); + let constness = print_constness_with_space( + &header.constness, + it.stable_since(tcx), + it.const_stability(tcx), + ); + let safety = header.safety.print_with_space(); + let abi = print_abi_with_space(header.abi).to_string(); + let asyncness = header.asyncness.print_with_space(); + let visibility = visibility_print_with_space(it, cx).to_string(); + let name = it.name.unwrap(); + + let generics_len = format!("{:#}", f.generics.print(cx)).len(); + let header_len = "fn ".len() + + visibility.len() + + constness.len() + + asyncness.len() + + safety.len() + + abi.len() + + name.as_str().len() + + generics_len; + + let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display(); + + wrap_item(w, |w| { + write!( + w, "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \ {name}{generics}{decl}{notable_traits}{where_clause}", attrs = render_attributes_in_pre(it, "", cx), @@ -631,35 +624,37 @@ fn item_function(w: &mut String, cx: &Context<'_>, it: &clean::Item, f: &clean:: abi = abi, name = name, generics = f.generics.print(cx), - where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), + where_clause = + print_where_clause(&f.generics, cx, 0, Ending::Newline).maybe_display(), decl = f.decl.full_print(header_len, 0, cx), - ), - ); - }); - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); + ) + })?; + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + }) } -fn item_trait(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { - let tcx = cx.tcx(); - let bounds = bounds(&t.bounds, false, cx); - let required_types = - t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>(); - let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); - let required_consts = - t.items.iter().filter(|m| m.is_required_associated_const()).collect::<Vec<_>>(); - let provided_consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); - let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); - let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); - let count_types = required_types.len() + provided_types.len(); - let count_consts = required_consts.len() + provided_consts.len(); - let count_methods = required_methods.len() + provided_methods.len(); - let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); - - // Output the trait definition - wrap_item(w, |mut w| { - write_str( - w, - format_args!( +fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display { + fmt::from_fn(|w| { + let tcx = cx.tcx(); + let bounds = bounds(&t.bounds, false, cx); + let required_types = + t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>(); + let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); + let required_consts = + t.items.iter().filter(|m| m.is_required_associated_const()).collect::<Vec<_>>(); + let provided_consts = + t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); + let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); + let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); + let count_types = required_types.len() + provided_types.len(); + let count_consts = required_consts.len() + provided_consts.len(); + let count_methods = required_methods.len() + provided_methods.len(); + let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); + + // Output the trait definition + wrap_item(w, |mut w| { + write!( + w, "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}", attrs = render_attributes_in_pre(it, "", cx), vis = visibility_print_with_space(it, cx), @@ -667,747 +662,808 @@ fn item_trait(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra is_auto = if t.is_auto(tcx) { "auto " } else { "" }, name = it.name.unwrap(), generics = t.generics.print(cx), - ), - ); - - if !t.generics.where_predicates.is_empty() { - write_str( - w, - format_args!("{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)), - ); - } else { - w.push_str(" "); - } + )?; - if t.items.is_empty() { - w.push_str("{ }"); - } else { - // FIXME: we should be using a derived_id for the Anchors here - w.push_str("{\n"); - let mut toggle = false; - - // If there are too many associated types, hide _everything_ - if should_hide_fields(count_types) { - toggle = true; - toggle_open( - &mut w, - format_args!("{} associated items", count_types + count_consts + count_methods), - ); + if !t.generics.where_predicates.is_empty() { + write!( + w, + "{}", + print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display() + )?; + } else { + w.write_char(' ')?; } - for types in [&required_types, &provided_types] { - for t in types { - render_assoc_item( - w, - t, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, + + if t.items.is_empty() { + w.write_str("{ }") + } else { + // FIXME: we should be using a derived_id for the Anchors here + w.write_str("{\n")?; + let mut toggle = false; + + // If there are too many associated types, hide _everything_ + if should_hide_fields(count_types) { + toggle = true; + toggle_open( + &mut w, + format_args!( + "{} associated items", + count_types + count_consts + count_methods + ), ); - w.push_str(";\n"); } - } - // If there are too many associated constants, hide everything after them - // We also do this if the types + consts is large because otherwise we could - // render a bunch of types and _then_ a bunch of consts just because both were - // _just_ under the limit - if !toggle && should_hide_fields(count_types + count_consts) { - toggle = true; - toggle_open( - &mut w, - format_args!( - "{count_consts} associated constant{plural_const} and \ + for types in [&required_types, &provided_types] { + for t in types { + writeln!( + w, + "{};", + render_assoc_item( + t, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ) + )?; + } + } + // If there are too many associated constants, hide everything after them + // We also do this if the types + consts is large because otherwise we could + // render a bunch of types and _then_ a bunch of consts just because both were + // _just_ under the limit + if !toggle && should_hide_fields(count_types + count_consts) { + toggle = true; + toggle_open( + &mut w, + format_args!( + "{count_consts} associated constant{plural_const} and \ {count_methods} method{plural_method}", - plural_const = pluralize(count_consts), - plural_method = pluralize(count_methods), - ), - ); - } - if count_types != 0 && (count_consts != 0 || count_methods != 0) { - w.push_str("\n"); - } - for consts in [&required_consts, &provided_consts] { - for c in consts { - render_assoc_item( - w, - c, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, + plural_const = pluralize(count_consts), + plural_method = pluralize(count_methods), + ), ); - w.push_str(";\n"); } - } - if !toggle && should_hide_fields(count_methods) { - toggle = true; - toggle_open(&mut w, format_args!("{count_methods} methods")); - } - if count_consts != 0 && count_methods != 0 { - w.push_str("\n"); - } - - if !required_methods.is_empty() { - write_str( - w, - format_args_nl!(" // Required method{}", pluralize(required_methods.len())), - ); - } - for (pos, m) in required_methods.iter().enumerate() { - render_assoc_item( - w, - m, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); - w.push_str(";\n"); + if count_types != 0 && (count_consts != 0 || count_methods != 0) { + w.write_str("\n")?; + } + for consts in [&required_consts, &provided_consts] { + for c in consts { + writeln!( + w, + "{};", + render_assoc_item( + c, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ) + )?; + } + } + if !toggle && should_hide_fields(count_methods) { + toggle = true; + toggle_open(&mut w, format_args!("{count_methods} methods")); + } + if count_consts != 0 && count_methods != 0 { + w.write_str("\n")?; + } - if pos < required_methods.len() - 1 { - w.push_str("<span class=\"item-spacer\"></span>"); + if !required_methods.is_empty() { + writeln!(w, " // Required method{}", pluralize(required_methods.len()))?; } - } - if !required_methods.is_empty() && !provided_methods.is_empty() { - w.push_str("\n"); - } + for (pos, m) in required_methods.iter().enumerate() { + writeln!( + w, + "{};", + render_assoc_item( + m, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ) + )?; - if !provided_methods.is_empty() { - write_str( - w, - format_args_nl!(" // Provided method{}", pluralize(provided_methods.len())), - ); - } - for (pos, m) in provided_methods.iter().enumerate() { - render_assoc_item( - w, - m, - AssocItemLink::Anchor(None), - ItemType::Trait, - cx, - RenderMode::Normal, - ); + if pos < required_methods.len() - 1 { + w.write_str("<span class=\"item-spacer\"></span>")?; + } + } + if !required_methods.is_empty() && !provided_methods.is_empty() { + w.write_str("\n")?; + } - w.push_str(" { ... }\n"); + if !provided_methods.is_empty() { + writeln!(w, " // Provided method{}", pluralize(provided_methods.len()))?; + } + for (pos, m) in provided_methods.iter().enumerate() { + writeln!( + w, + "{} {{ ... }}", + render_assoc_item( + m, + AssocItemLink::Anchor(None), + ItemType::Trait, + cx, + RenderMode::Normal, + ) + )?; - if pos < provided_methods.len() - 1 { - w.push_str("<span class=\"item-spacer\"></span>"); + if pos < provided_methods.len() - 1 { + w.write_str("<span class=\"item-spacer\"></span>")?; + } } + if toggle { + toggle_close(&mut w); + } + w.write_str("}") } - if toggle { - toggle_close(&mut w); - } - w.push_str("}"); - } - }); + })?; - // Trait documentation - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); + // Trait documentation + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - fn trait_item(w: &mut String, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) { - let name = m.name.unwrap(); - info!("Documenting {name} on {ty_name:?}", ty_name = t.name); - let item_type = m.type_(); - let id = cx.derive_id(format!("{item_type}.{name}")); + fn trait_item(cx: &Context<'_>, m: &clean::Item, t: &clean::Item) -> impl fmt::Display { + fmt::from_fn(|w| { + let name = m.name.unwrap(); + info!("Documenting {name} on {ty_name:?}", ty_name = t.name); + let item_type = m.type_(); + let id = cx.derive_id(format!("{item_type}.{name}")); - let mut content = String::new(); - write_str(&mut content, format_args!("{}", document_full(m, cx, HeadingOffset::H5))); + let content = document_full(m, cx, HeadingOffset::H5).to_string(); - let toggled = !content.is_empty(); - if toggled { - let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write_str( - w, - format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"), - ); - } - write_str(w, format_args!("<section id=\"{id}\" class=\"method\">")); - render_rightside(w, cx, m, RenderMode::Normal); - write_str(w, format_args!("<h4 class=\"code-header\">")); - render_assoc_item( - w, - m, - AssocItemLink::Anchor(Some(&id)), - ItemType::Impl, - cx, - RenderMode::Normal, - ); - w.push_str("</h4></section>"); - document_item_info(cx, m, Some(t)).render_into(w).unwrap(); - if toggled { - write_str(w, format_args!("</summary>")); - w.push_str(&content); - write_str(w, format_args!("</details>")); + let toggled = !content.is_empty(); + if toggled { + let method_toggle_class = + if item_type.is_method() { " method-toggle" } else { "" }; + write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?; + } + write!( + w, + "<section id=\"{id}\" class=\"method\">\ + {}\ + <h4 class=\"code-header\">{}</h4></section>", + render_rightside(cx, m, RenderMode::Normal), + render_assoc_item( + m, + AssocItemLink::Anchor(Some(&id)), + ItemType::Impl, + cx, + RenderMode::Normal, + ) + )?; + document_item_info(cx, m, Some(t)).render_into(w).unwrap(); + if toggled { + write!(w, "</summary>{content}</details>")?; + } + Ok(()) + }) } - } - if !required_consts.is_empty() { - write_section_heading( - w, - "Required Associated Constants", - "required-associated-consts", - None, - "<div class=\"methods\">", - ); - for t in required_consts { - trait_item(w, cx, t, it); + if !required_consts.is_empty() { + write!( + w, + "{}", + write_section_heading( + "Required Associated Constants", + "required-associated-consts", + None, + "<div class=\"methods\">", + ) + )?; + for t in required_consts { + write!(w, "{}", trait_item(cx, t, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - if !provided_consts.is_empty() { - write_section_heading( - w, - "Provided Associated Constants", - "provided-associated-consts", - None, - "<div class=\"methods\">", - ); - for t in provided_consts { - trait_item(w, cx, t, it); + if !provided_consts.is_empty() { + write!( + w, + "{}", + write_section_heading( + "Provided Associated Constants", + "provided-associated-consts", + None, + "<div class=\"methods\">", + ) + )?; + for t in provided_consts { + write!(w, "{}", trait_item(cx, t, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - if !required_types.is_empty() { - write_section_heading( - w, - "Required Associated Types", - "required-associated-types", - None, - "<div class=\"methods\">", - ); - for t in required_types { - trait_item(w, cx, t, it); + if !required_types.is_empty() { + write!( + w, + "{}", + write_section_heading( + "Required Associated Types", + "required-associated-types", + None, + "<div class=\"methods\">", + ) + )?; + for t in required_types { + write!(w, "{}", trait_item(cx, t, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - if !provided_types.is_empty() { - write_section_heading( - w, - "Provided Associated Types", - "provided-associated-types", - None, - "<div class=\"methods\">", - ); - for t in provided_types { - trait_item(w, cx, t, it); + if !provided_types.is_empty() { + write!( + w, + "{}", + write_section_heading( + "Provided Associated Types", + "provided-associated-types", + None, + "<div class=\"methods\">", + ) + )?; + for t in provided_types { + write!(w, "{}", trait_item(cx, t, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - - // Output the documentation for each function individually - if !required_methods.is_empty() || must_implement_one_of_functions.is_some() { - write_section_heading( - w, - "Required Methods", - "required-methods", - None, - "<div class=\"methods\">", - ); - if let Some(list) = must_implement_one_of_functions.as_deref() { - write_str( + // Output the documentation for each function individually + if !required_methods.is_empty() || must_implement_one_of_functions.is_some() { + write!( w, - format_args!( + "{}", + write_section_heading( + "Required Methods", + "required-methods", + None, + "<div class=\"methods\">", + ) + )?; + + if let Some(list) = must_implement_one_of_functions.as_deref() { + write!( + w, "<div class=\"stab must_implement\">At least one of the `{}` methods is required.</div>", - fmt::from_fn(|f| list.iter().joined("`, `", f)) - ), - ); - } + fmt::from_fn(|f| list.iter().joined("`, `", f)), + )?; + } - for m in required_methods { - trait_item(w, cx, m, it); + for m in required_methods { + write!(w, "{}", trait_item(cx, m, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - if !provided_methods.is_empty() { - write_section_heading( - w, - "Provided Methods", - "provided-methods", - None, - "<div class=\"methods\">", - ); - for m in provided_methods { - trait_item(w, cx, m, it); + if !provided_methods.is_empty() { + write!( + w, + "{}", + write_section_heading( + "Provided Methods", + "provided-methods", + None, + "<div class=\"methods\">", + ) + )?; + for m in provided_methods { + write!(w, "{}", trait_item(cx, m, it))?; + } + w.write_str("</div>")?; } - w.push_str("</div>"); - } - // If there are methods directly on this trait object, render them here. - write_str( - w, - format_args!( + // If there are methods directly on this trait object, render them here. + write!( + w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All) - ), - ); + )?; - let mut extern_crates = FxIndexSet::default(); + let mut extern_crates = FxIndexSet::default(); - if !t.is_dyn_compatible(cx.tcx()) { - write_section_heading( - w, - "Dyn Compatibility", - "dyn-compatibility", - None, - format!( - "<div class=\"dyn-compatibility-info\"><p>This trait is <b>not</b> \ - <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\ - <p><i>In older versions of Rust, dyn compatibility was called \"object safety\", \ - so this trait is not object safe.</i></p></div>", - base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION - ), - ); - } + if !t.is_dyn_compatible(cx.tcx()) { + write!( + w, + "{}", + write_section_heading( + "Dyn Compatibility", + "dyn-compatibility", + None, + format!( + "<div class=\"dyn-compatibility-info\"><p>This trait is <b>not</b> \ + <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\ + <p><i>In older versions of Rust, dyn compatibility was called \"object safety\", \ + so this trait is not object safe.</i></p></div>", + base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION + ), + ), + )?; + } - if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) { - // The DefId is for the first Type found with that name. The bool is - // if any Types with the same name but different DefId have been found. - let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); - for implementor in implementors { - if let Some(did) = - implementor.inner_impl().for_.without_borrowed_ref().def_id(&cx.shared.cache) - && !did.is_local() - { - extern_crates.insert(did.krate); - } - match implementor.inner_impl().for_.without_borrowed_ref() { - clean::Type::Path { ref path } if !path.is_assoc_ty() => { - let did = path.def_id(); - let &mut (prev_did, ref mut has_duplicates) = - implementor_dups.entry(path.last()).or_insert((did, false)); - if prev_did != did { - *has_duplicates = true; + if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) { + // The DefId is for the first Type found with that name. The bool is + // if any Types with the same name but different DefId have been found. + let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); + for implementor in implementors { + if let Some(did) = + implementor.inner_impl().for_.without_borrowed_ref().def_id(&cx.shared.cache) + && !did.is_local() + { + extern_crates.insert(did.krate); + } + match implementor.inner_impl().for_.without_borrowed_ref() { + clean::Type::Path { path } if !path.is_assoc_ty() => { + let did = path.def_id(); + let &mut (prev_did, ref mut has_duplicates) = + implementor_dups.entry(path.last()).or_insert((did, false)); + if prev_did != did { + *has_duplicates = true; + } } + _ => {} } - _ => {} } - } - - let (local, mut foreign) = - implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx)); - let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = - local.iter().partition(|i| i.inner_impl().kind.is_auto()); + let (local, mut foreign) = + implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx)); - synthetic.sort_by_cached_key(|i| ImplString::new(i, cx)); - concrete.sort_by_cached_key(|i| ImplString::new(i, cx)); - foreign.sort_by_cached_key(|i| ImplString::new(i, cx)); + let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = + local.iter().partition(|i| i.inner_impl().kind.is_auto()); - if !foreign.is_empty() { - write_section_heading(w, "Implementations on Foreign Types", "foreign-impls", None, ""); + synthetic.sort_by_cached_key(|i| ImplString::new(i, cx)); + concrete.sort_by_cached_key(|i| ImplString::new(i, cx)); + foreign.sort_by_cached_key(|i| ImplString::new(i, cx)); - for implementor in foreign { - let provided_methods = implementor.inner_impl().provided_trait_methods(tcx); - let assoc_link = - AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods); - render_impl( + if !foreign.is_empty() { + write!( w, - cx, - implementor, - it, - assoc_link, - RenderMode::Normal, - None, - &[], - ImplRenderingParameters { - show_def_docs: false, - show_default_items: false, - show_non_assoc_items: true, - toggle_open_by_default: false, - }, - ); - } - } + "{}", + write_section_heading( + "Implementations on Foreign Types", + "foreign-impls", + None, + "" + ) + )?; - write_section_heading( - w, - "Implementors", - "implementors", - None, - "<div id=\"implementors-list\">", - ); - for implementor in concrete { - render_implementor(cx, implementor, it, w, &implementor_dups, &[]); - } - w.push_str("</div>"); + for implementor in foreign { + let provided_methods = implementor.inner_impl().provided_trait_methods(tcx); + let assoc_link = + AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods); + write!( + w, + "{}", + render_impl( + cx, + implementor, + it, + assoc_link, + RenderMode::Normal, + None, + &[], + ImplRenderingParameters { + show_def_docs: false, + show_default_items: false, + show_non_assoc_items: true, + toggle_open_by_default: false, + }, + ) + )?; + } + } - if t.is_auto(tcx) { - write_section_heading( + write!( w, - "Auto implementors", - "synthetic-implementors", - None, - "<div id=\"synthetic-implementors-list\">", - ); - for implementor in synthetic { - render_implementor( - cx, - implementor, - it, - w, - &implementor_dups, - &collect_paths_for_type( - implementor.inner_impl().for_.clone(), - &cx.shared.cache, - ), - ); + "{}", + write_section_heading( + "Implementors", + "implementors", + None, + "<div id=\"implementors-list\">", + ) + )?; + for implementor in concrete { + write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?; } - w.push_str("</div>"); - } - } else { - // even without any implementations to write in, we still want the heading and list, so the - // implementors javascript file pulled in below has somewhere to write the impls into - write_section_heading( - w, - "Implementors", - "implementors", - None, - "<div id=\"implementors-list\"></div>", - ); + w.write_str("</div>")?; - if t.is_auto(tcx) { - write_section_heading( + if t.is_auto(tcx) { + write!( + w, + "{}", + write_section_heading( + "Auto implementors", + "synthetic-implementors", + None, + "<div id=\"synthetic-implementors-list\">", + ) + )?; + for implementor in synthetic { + write!( + w, + "{}", + render_implementor( + cx, + implementor, + it, + &implementor_dups, + &collect_paths_for_type( + implementor.inner_impl().for_.clone(), + &cx.shared.cache, + ), + ) + )?; + } + w.write_str("</div>")?; + } + } else { + // even without any implementations to write in, we still want the heading and list, so the + // implementors javascript file pulled in below has somewhere to write the impls into + write!( w, - "Auto implementors", - "synthetic-implementors", - None, - "<div id=\"synthetic-implementors-list\"></div>", - ); - } - } + "{}", + write_section_heading( + "Implementors", + "implementors", + None, + "<div id=\"implementors-list\"></div>", + ) + )?; - // [RUSTDOCIMPL] trait.impl - // - // Include implementors in crates that depend on the current crate. - // - // This is complicated by the way rustdoc is invoked, which is basically - // the same way rustc is invoked: it gets called, one at a time, for each - // crate. When building the rustdocs for the current crate, rustdoc can - // see crate metadata for its dependencies, but cannot see metadata for its - // dependents. - // - // To make this work, we generate a "hook" at this stage, and our - // dependents can "plug in" to it when they build. For simplicity's sake, - // it's [JSONP]: a JavaScript file with the data we need (and can parse), - // surrounded by a tiny wrapper that the Rust side ignores, but allows the - // JavaScript side to include without having to worry about Same Origin - // Policy. The code for *that* is in `write_shared.rs`. - // - // This is further complicated by `#[doc(inline)]`. We want all copies - // of an inlined trait to reference the same JS file, to address complex - // dependency graphs like this one (lower crates depend on higher crates): - // - // ```text - // -------------------------------------------- - // | crate A: trait Foo | - // -------------------------------------------- - // | | - // -------------------------------- | - // | crate B: impl A::Foo for Bar | | - // -------------------------------- | - // | | - // --------------------------------------------- - // | crate C: #[doc(inline)] use A::Foo as Baz | - // | impl Baz for Quux | - // --------------------------------------------- - // ``` - // - // Basically, we want `C::Baz` and `A::Foo` to show the same set of - // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js` - // as the Single Source of Truth. - // - // We also want the `impl Baz for Quux` to be written to - // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`, - // we're going to want to generate plain HTML for `impl Baz for Quux` too, - // because that'll load faster, and it's better for SEO. And we don't want - // the same impl to show up twice on the same page. - // - // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda - // like this: - // - // ```js - // JSONP({ - // "B": {"impl A::Foo for Bar"}, - // "C": {"impl Baz for Quux"}, - // }); - // ``` - // - // First of all, this means we can rebuild a crate, and it'll replace its own - // data if something changes. That is, `rustdoc` is idempotent. The other - // advantage is that we can list the crates that get included in the HTML, - // and ignore them when doing the JavaScript-based part of rendering. - // So C's HTML will have something like this: - // - // ```html - // <script src="/trait.impl/A/trait.Foo.js" - // data-ignore-extern-crates="A,B" async></script> - // ``` - // - // And, when the JS runs, anything in data-ignore-extern-crates is known - // to already be in the HTML, and will be ignored. - // - // [JSONP]: https://en.wikipedia.org/wiki/JSONP - let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") - .take(cx.current.len()) - .chain(std::iter::once("trait.impl")) - .collect(); - if let Some(did) = it.item_id.as_def_id() - && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) } - && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern) - { - js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); - js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); - } else { - js_src_path.extend(cx.current.iter().copied()); - js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); - } - let extern_crates = fmt::from_fn(|f| { - if !extern_crates.is_empty() { - f.write_str(" data-ignore-extern-crates=\"")?; - extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?; - f.write_str("\"")?; + if t.is_auto(tcx) { + write!( + w, + "{}", + write_section_heading( + "Auto implementors", + "synthetic-implementors", + None, + "<div id=\"synthetic-implementors-list\"></div>", + ) + )?; + } } - Ok(()) - }); - write_str( - w, - format_args!( + + // [RUSTDOCIMPL] trait.impl + // + // Include implementors in crates that depend on the current crate. + // + // This is complicated by the way rustdoc is invoked, which is basically + // the same way rustc is invoked: it gets called, one at a time, for each + // crate. When building the rustdocs for the current crate, rustdoc can + // see crate metadata for its dependencies, but cannot see metadata for its + // dependents. + // + // To make this work, we generate a "hook" at this stage, and our + // dependents can "plug in" to it when they build. For simplicity's sake, + // it's [JSONP]: a JavaScript file with the data we need (and can parse), + // surrounded by a tiny wrapper that the Rust side ignores, but allows the + // JavaScript side to include without having to worry about Same Origin + // Policy. The code for *that* is in `write_shared.rs`. + // + // This is further complicated by `#[doc(inline)]`. We want all copies + // of an inlined trait to reference the same JS file, to address complex + // dependency graphs like this one (lower crates depend on higher crates): + // + // ```text + // -------------------------------------------- + // | crate A: trait Foo | + // -------------------------------------------- + // | | + // -------------------------------- | + // | crate B: impl A::Foo for Bar | | + // -------------------------------- | + // | | + // --------------------------------------------- + // | crate C: #[doc(inline)] use A::Foo as Baz | + // | impl Baz for Quux | + // --------------------------------------------- + // ``` + // + // Basically, we want `C::Baz` and `A::Foo` to show the same set of + // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js` + // as the Single Source of Truth. + // + // We also want the `impl Baz for Quux` to be written to + // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`, + // we're going to want to generate plain HTML for `impl Baz for Quux` too, + // because that'll load faster, and it's better for SEO. And we don't want + // the same impl to show up twice on the same page. + // + // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda + // like this: + // + // ```js + // JSONP({ + // "B": {"impl A::Foo for Bar"}, + // "C": {"impl Baz for Quux"}, + // }); + // ``` + // + // First of all, this means we can rebuild a crate, and it'll replace its own + // data if something changes. That is, `rustdoc` is idempotent. The other + // advantage is that we can list the crates that get included in the HTML, + // and ignore them when doing the JavaScript-based part of rendering. + // So C's HTML will have something like this: + // + // ```html + // <script src="/trait.impl/A/trait.Foo.js" + // data-ignore-extern-crates="A,B" async></script> + // ``` + // + // And, when the JS runs, anything in data-ignore-extern-crates is known + // to already be in the HTML, and will be ignored. + // + // [JSONP]: https://en.wikipedia.org/wiki/JSONP + let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") + .take(cx.current.len()) + .chain(std::iter::once("trait.impl")) + .collect(); + if let Some(did) = it.item_id.as_def_id() + && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) } + && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern) + { + js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); + js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); + } else { + js_src_path.extend(cx.current.iter().copied()); + js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); + } + let extern_crates = fmt::from_fn(|f| { + if !extern_crates.is_empty() { + f.write_str(" data-ignore-extern-crates=\"")?; + extern_crates.iter().map(|&cnum| tcx.crate_name(cnum)).joined(",", f)?; + f.write_str("\"")?; + } + Ok(()) + }); + write!( + w, "<script src=\"{src}\"{extern_crates} async></script>", src = js_src_path.finish() - ), - ); + ) + }) } fn item_trait_alias( - w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias, -) { - wrap_item(w, |w| { +) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + write!( + w, + "{attrs}trait {name}{generics}{where_b} = {bounds};", + attrs = render_attributes_in_pre(it, "", cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), + bounds = bounds(&t.bounds, true, cx), + ) + })?; + + 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 + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. write!( w, - "{attrs}trait {name}{generics}{where_b} = {bounds};", - attrs = render_attributes_in_pre(it, "", cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline), - bounds = bounds(&t.bounds, true, cx), + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All) ) - .unwrap(); - }); - - 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_type_alias(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - wrap_item(w, |w| { - write_str( - w, - format_args!( +fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + write!( + w, "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", attrs = render_attributes_in_pre(it, "", cx), vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + where_clause = + print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), type_ = t.type_.print(cx), - ), - ); - }); + ) + })?; - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - if let Some(inner_type) = &t.inner_type { - write_section_heading(w, "Aliased Type", "aliased-type", None, ""); + if let Some(inner_type) = &t.inner_type { + write!(w, "{}", write_section_heading("Aliased Type", "aliased-type", None, ""),)?; - match inner_type { - clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); - let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); - let enum_def_id = ty.ty_adt_def().unwrap().did(); + match inner_type { + clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { + let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let enum_def_id = ty.ty_adt_def().unwrap().did(); - wrap_item(w, |w| { - let variants_len = variants.len(); - let variants_count = variants_iter().count(); - let has_stripped_entries = variants_len != variants_count; + wrap_item(w, |w| { + let variants_len = variants.len(); + let variants_count = variants_iter().count(); + let has_stripped_entries = variants_len != variants_count; - write_str(w, format_args!("enum {}{}", it.name.unwrap(), t.generics.print(cx))); - render_enum_fields( - w, - cx, - Some(&t.generics), - variants, - variants_count, - has_stripped_entries, - *is_non_exhaustive, - enum_def_id, - ) - }); - item_variants(w, cx, it, variants, enum_def_id); - } - clean::TypeAliasInnerType::Union { fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; + write!( + w, + "enum {}{}{}", + it.name.unwrap(), + t.generics.print(cx), + render_enum_fields( + cx, + Some(&t.generics), + variants, + variants_count, + has_stripped_entries, + *is_non_exhaustive, + enum_def_id, + ) + ) + })?; + write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?; + } + clean::TypeAliasInnerType::Union { fields } => { + wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; - write_str( - w, - format_args!("union {}{}", it.name.unwrap(), t.generics.print(cx)), - ); - render_struct_fields( - w, - Some(&t.generics), - None, - fields, - "", - true, - has_stripped_fields, - cx, - ); - }); - item_fields(w, cx, it, fields, None); - } - clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; + write!( + w, + "union {}{}{}", + it.name.unwrap(), + t.generics.print(cx), + render_struct_fields( + Some(&t.generics), + None, + fields, + "", + true, + has_stripped_fields, + cx, + ), + ) + })?; + write!(w, "{}", item_fields(cx, it, fields, None))?; + } + clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { + wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; - write_str( - w, - format_args!("struct {}{}", it.name.unwrap(), t.generics.print(cx)), - ); - render_struct_fields( - w, - Some(&t.generics), - *ctor_kind, - fields, - "", - true, - has_stripped_fields, - cx, - ); - }); - item_fields(w, cx, it, fields, None); + write!( + w, + "struct {}{}{}", + it.name.unwrap(), + t.generics.print(cx), + render_struct_fields( + Some(&t.generics), + *ctor_kind, + fields, + "", + true, + has_stripped_fields, + cx, + ), + ) + })?; + write!(w, "{}", item_fields(cx, it, fields, None))?; + } } } - } - let def_id = it.item_id.expect_def_id(); - // 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_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))); - write_str(w, format_args!("{}", document_type_layout(cx, def_id))); - - // [RUSTDOCIMPL] type.impl - // - // Include type definitions from the alias target type. - // - // Earlier versions of this code worked by having `render_assoc_items` - // include this data directly. That generates *O*`(types*impls)` of HTML - // text, and some real crates have a lot of types and impls. - // - // To create the same UX without generating half a gigabyte of HTML for a - // crate that only contains 20 megabytes of actual documentation[^115718], - // rustdoc stashes these type-alias-inlined docs in a [JSONP] - // "database-lite". The file itself is generated in `write_shared.rs`, - // and hooks into functions provided by `main.js`. - // - // The format of `trait.impl` and `type.impl` JS files are superficially - // similar. Each line, except the JSONP wrapper itself, belongs to a crate, - // and they are otherwise separate (rustdoc should be idempotent). The - // "meat" of the file is HTML strings, so the frontend code is very simple. - // Links are relative to the doc root, though, so the frontend needs to fix - // that up, and inlined docs can reuse these files. - // - // However, there are a few differences, caused by the sophisticated - // features that type aliases have. Consider this crate graph: - // - // ```text - // --------------------------------- - // | crate A: struct Foo<T> | - // | type Bar = Foo<i32> | - // | impl X for Foo<i8> | - // | impl Y for Foo<i32> | - // --------------------------------- - // | - // ---------------------------------- - // | crate B: type Baz = A::Foo<i8> | - // | type Xyy = A::Foo<i8> | - // | impl Z for Xyy | - // ---------------------------------- - // ``` - // - // The type.impl/A/struct.Foo.js JS file has a structure kinda like this: - // - // ```js - // JSONP({ - // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]], - // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]], - // }); - // ``` - // - // When the type.impl file is loaded, only the current crate's docs are - // actually used. The main reason to bundle them together is that there's - // enough duplication in them for DEFLATE to remove the redundancy. - // - // The contents of a crate are a list of impl blocks, themselves - // represented as lists. The first item in the sublist is the HTML block, - // the second item is the name of the trait (which goes in the sidebar), - // and all others are the names of type aliases that successfully match. - // - // This way: - // - // - There's no need to generate these files for types that have no aliases - // in the current crate. If a dependent crate makes a type alias, it'll - // take care of generating its own docs. - // - There's no need to reimplement parts of the type checker in - // JavaScript. The Rust backend does the checking, and includes its - // results in the file. - // - Docs defined directly on the type alias are dropped directly in the - // HTML by `render_assoc_items`, and are accessible without JavaScript. - // The JSONP file will not list impl items that are known to be part - // of the main HTML file already. - // - // [JSONP]: https://en.wikipedia.org/wiki/JSONP - // [^115718]: https://github.com/rust-lang/rust/issues/115718 - let cache = &cx.shared.cache; - if let Some(target_did) = t.type_.def_id(cache) && - let get_extern = { || cache.external_paths.get(&target_did) } && - let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) && - target_type.is_adt() && // primitives cannot be inlined - let Some(self_did) = it.item_id.as_def_id() && - let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } && - let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) - { - let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") - .take(cx.current.len()) - .chain(std::iter::once("type.impl")) - .collect(); - js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied()); - js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap())); - let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f)); - write_str( + let def_id = it.item_id.expect_def_id(); + // 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, - format_args!( + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + )?; + + // [RUSTDOCIMPL] type.impl + // + // Include type definitions from the alias target type. + // + // Earlier versions of this code worked by having `render_assoc_items` + // include this data directly. That generates *O*`(types*impls)` of HTML + // text, and some real crates have a lot of types and impls. + // + // To create the same UX without generating half a gigabyte of HTML for a + // crate that only contains 20 megabytes of actual documentation[^115718], + // rustdoc stashes these type-alias-inlined docs in a [JSONP] + // "database-lite". The file itself is generated in `write_shared.rs`, + // and hooks into functions provided by `main.js`. + // + // The format of `trait.impl` and `type.impl` JS files are superficially + // similar. Each line, except the JSONP wrapper itself, belongs to a crate, + // and they are otherwise separate (rustdoc should be idempotent). The + // "meat" of the file is HTML strings, so the frontend code is very simple. + // Links are relative to the doc root, though, so the frontend needs to fix + // that up, and inlined docs can reuse these files. + // + // However, there are a few differences, caused by the sophisticated + // features that type aliases have. Consider this crate graph: + // + // ```text + // --------------------------------- + // | crate A: struct Foo<T> | + // | type Bar = Foo<i32> | + // | impl X for Foo<i8> | + // | impl Y for Foo<i32> | + // --------------------------------- + // | + // ---------------------------------- + // | crate B: type Baz = A::Foo<i8> | + // | type Xyy = A::Foo<i8> | + // | impl Z for Xyy | + // ---------------------------------- + // ``` + // + // The type.impl/A/struct.Foo.js JS file has a structure kinda like this: + // + // ```js + // JSONP({ + // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]], + // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]], + // }); + // ``` + // + // When the type.impl file is loaded, only the current crate's docs are + // actually used. The main reason to bundle them together is that there's + // enough duplication in them for DEFLATE to remove the redundancy. + // + // The contents of a crate are a list of impl blocks, themselves + // represented as lists. The first item in the sublist is the HTML block, + // the second item is the name of the trait (which goes in the sidebar), + // and all others are the names of type aliases that successfully match. + // + // This way: + // + // - There's no need to generate these files for types that have no aliases + // in the current crate. If a dependent crate makes a type alias, it'll + // take care of generating its own docs. + // - There's no need to reimplement parts of the type checker in + // JavaScript. The Rust backend does the checking, and includes its + // results in the file. + // - Docs defined directly on the type alias are dropped directly in the + // HTML by `render_assoc_items`, and are accessible without JavaScript. + // The JSONP file will not list impl items that are known to be part + // of the main HTML file already. + // + // [JSONP]: https://en.wikipedia.org/wiki/JSONP + // [^115718]: https://github.com/rust-lang/rust/issues/115718 + let cache = &cx.shared.cache; + if let Some(target_did) = t.type_.def_id(cache) + && let get_extern = { || cache.external_paths.get(&target_did) } + && let Some(&(ref target_fqp, target_type)) = + cache.paths.get(&target_did).or_else(get_extern) + && target_type.is_adt() // primitives cannot be inlined + && let Some(self_did) = it.item_id.as_def_id() + && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } + && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) + { + let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") + .take(cx.current.len()) + .chain(std::iter::once("type.impl")) + .collect(); + js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied()); + js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap())); + let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f)); + write!( + w, "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>", - src = js_src_path.finish() - ), - ); - } + src = js_src_path.finish(), + )?; + } + Ok(()) + }) } -fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { item_template!( #[template(path = "item_union.html")] struct ItemUnion<'a, 'cx> { @@ -1419,35 +1475,20 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union<'b>(&'b self) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx); - write!(f, "{v}") - }) + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) } - fn document_field<'b>( - &'b self, - field: &'a clean::Item, - ) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = document(self.cx, field, Some(self.it), HeadingOffset::H3); - write!(f, "{v}") - }) + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) } fn stability_field(&self, field: &clean::Item) -> Option<String> { field.stability_class(self.cx.tcx()) } - fn print_ty<'b>( - &'b self, - ty: &'a clean::Type, - ) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = ty.print(self.cx); - write!(f, "{v}") - }) + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) } fn fields_iter( @@ -1464,13 +1505,13 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni } } - ItemUnion { cx, it, s }.render_into(w).unwrap(); + fmt::from_fn(|w| { + ItemUnion { cx, it, s }.render_into(w).unwrap(); + Ok(()) + }) } -fn print_tuple_struct_fields<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - s: &'a [clean::Item], -) -> impl Display + 'a + Captures<'cx> { +fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display { fmt::from_fn(|f| { if !s.is_empty() && s.iter().all(|field| { @@ -1492,40 +1533,42 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( }) } -fn item_enum(w: &mut String, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { - let count_variants = e.variants().count(); - wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); - write_str( - w, - format_args!( - "{}enum {}{}", +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { + fmt::from_fn(|w| { + let count_variants = e.variants().count(); + wrap_item(w, |w| { + render_attributes_in_code(w, it, cx); + write!( + w, + "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), e.generics.print(cx), - ), - ); - - render_enum_fields( - w, - cx, - Some(&e.generics), - &e.variants, - count_variants, - e.has_stripped_entries(), - it.is_non_exhaustive(), - it.def_id().unwrap(), - ); - }); + render_enum_fields( + cx, + Some(&e.generics), + &e.variants, + count_variants, + e.has_stripped_entries(), + it.is_non_exhaustive(), + it.def_id().unwrap(), + ), + ) + })?; - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - if count_variants != 0 { - item_variants(w, cx, it, &e.variants, it.def_id().unwrap()); - } - let def_id = it.item_id.expect_def_id(); - write_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))); - write_str(w, format_args!("{}", document_type_layout(cx, def_id))); + if count_variants != 0 { + write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?; + } + let def_id = it.item_id.expect_def_id(); + write!( + w, + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + ) + }) } /// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at @@ -1554,32 +1597,33 @@ fn should_show_enum_discriminant( } fn display_c_like_variant( - w: &mut String, cx: &Context<'_>, item: &clean::Item, variant: &clean::Variant, index: VariantIdx, should_show_enum_discriminant: bool, enum_def_id: DefId, -) { - let name = item.name.unwrap(); - if let Some(ref value) = variant.discriminant { - write_str(w, format_args!("{} = {}", name.as_str(), value.value(cx.tcx(), true))); - } else if should_show_enum_discriminant { - let adt_def = cx.tcx().adt_def(enum_def_id); - let discr = adt_def.discriminant_for_variant(cx.tcx(), index); - if discr.ty.is_signed() { - write_str(w, format_args!("{} = {}", name.as_str(), discr.val as i128)); +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let name = item.name.unwrap(); + if let Some(ref value) = variant.discriminant { + write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true))?; + } else if should_show_enum_discriminant { + let adt_def = cx.tcx().adt_def(enum_def_id); + let discr = adt_def.discriminant_for_variant(cx.tcx(), index); + if discr.ty.is_signed() { + write!(w, "{} = {}", name.as_str(), discr.val as i128)?; + } else { + write!(w, "{} = {}", name.as_str(), discr.val)?; + } } else { - write_str(w, format_args!("{} = {}", name.as_str(), discr.val)); + write!(w, "{name}")?; } - } else { - w.push_str(name.as_str()); - } + Ok(()) + }) } fn render_enum_fields( - mut w: &mut String, cx: &Context<'_>, g: Option<&clean::Generics>, variants: &IndexVec<VariantIdx, clean::Item>, @@ -1587,422 +1631,452 @@ fn render_enum_fields( has_stripped_entries: bool, is_non_exhaustive: bool, enum_def_id: DefId, -) { - let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants); - if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) { - // If there wasn't a `where` clause, we add a whitespace. - w.push_str(" "); - } - - let variants_stripped = has_stripped_entries; - if count_variants == 0 && !variants_stripped { - w.push_str("{}"); - } else { - w.push_str("{\n"); - let toggle = should_hide_fields(count_variants); - if toggle { - toggle_open(&mut w, format_args!("{count_variants} variants")); +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let should_show_enum_discriminant = + should_show_enum_discriminant(cx, enum_def_id, variants); + if let Some(generics) = g + && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) + { + write!(w, "{where_clause}")?; + } else { + // If there wasn't a `where` clause, we add a whitespace. + w.write_char(' ')?; } - const TAB: &str = " "; - for (index, v) in variants.iter_enumerated() { - if v.is_stripped() { - continue; + + let variants_stripped = has_stripped_entries; + if count_variants == 0 && !variants_stripped { + w.write_str("{}") + } else { + w.write_str("{\n")?; + let toggle = should_hide_fields(count_variants); + if toggle { + toggle_open(&mut *w, format_args!("{count_variants} variants")); } - w.push_str(TAB); - match v.kind { - clean::VariantItem(ref var) => match var.kind { - clean::VariantKind::CLike => display_c_like_variant( - w, - cx, - v, - var, - index, - should_show_enum_discriminant, - enum_def_id, - ), - clean::VariantKind::Tuple(ref s) => { - write_str( - w, - format_args!( - "{}({})", - v.name.unwrap(), - print_tuple_struct_fields(cx, s) - ), - ); - } - clean::VariantKind::Struct(ref s) => { - render_struct(w, v, None, None, &s.fields, TAB, false, cx); - } - }, - _ => unreachable!(), + const TAB: &str = " "; + for (index, v) in variants.iter_enumerated() { + if v.is_stripped() { + continue; + } + w.write_str(TAB)?; + match v.kind { + clean::VariantItem(ref var) => match var.kind { + clean::VariantKind::CLike => { + write!( + w, + "{}", + display_c_like_variant( + cx, + v, + var, + index, + should_show_enum_discriminant, + enum_def_id, + ) + )?; + } + clean::VariantKind::Tuple(ref s) => { + write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s))?; + } + clean::VariantKind::Struct(ref s) => { + write!( + w, + "{}", + render_struct(v, None, None, &s.fields, TAB, false, cx) + )?; + } + }, + _ => unreachable!(), + } + w.write_str(",\n")?; } - w.push_str(",\n"); - } - if variants_stripped && !is_non_exhaustive { - w.push_str(" <span class=\"comment\">// some variants omitted</span>\n"); - } - if toggle { - toggle_close(&mut w); + if variants_stripped && !is_non_exhaustive { + w.write_str(" <span class=\"comment\">// some variants omitted</span>\n")?; + } + if toggle { + toggle_close(&mut *w); + } + w.write_str("}") } - w.push_str("}"); - } + }) } fn item_variants( - w: &mut String, cx: &Context<'_>, it: &clean::Item, variants: &IndexVec<VariantIdx, clean::Item>, enum_def_id: DefId, -) { - let tcx = cx.tcx(); - write_section_heading( - w, - &format!("Variants{}", document_non_exhaustive_header(it)), - "variants", - Some("variants"), - format!("{}<div class=\"variants\">", document_non_exhaustive(it)), - ); - - let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants); - for (index, variant) in variants.iter_enumerated() { - if variant.is_stripped() { - continue; - } - let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); - write_str( +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let tcx = cx.tcx(); + write!( w, - format_args!( - "<section id=\"{id}\" class=\"variant\">\ - <a href=\"#{id}\" class=\"anchor\">ยง</a>" + "{}", + write_section_heading( + &format!("Variants{}", document_non_exhaustive_header(it)), + "variants", + Some("variants"), + format!("{}<div class=\"variants\">", document_non_exhaustive(it)), ), - ); - render_stability_since_raw_with_extra( - w, - variant.stable_since(tcx), - variant.const_stability(tcx), - " rightside", - ); - w.push_str("<h3 class=\"code-header\">"); - if let clean::VariantItem(ref var) = variant.kind - && let clean::VariantKind::CLike = var.kind - { - display_c_like_variant( + )?; + + let should_show_enum_discriminant = + should_show_enum_discriminant(cx, enum_def_id, variants); + for (index, variant) in variants.iter_enumerated() { + if variant.is_stripped() { + continue; + } + let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); + write!( w, - cx, - variant, - var, - index, - should_show_enum_discriminant, - enum_def_id, - ); - } else { - w.push_str(variant.name.unwrap().as_str()); - } + "<section id=\"{id}\" class=\"variant\">\ + <a href=\"#{id}\" class=\"anchor\">ยง</a>\ + {}\ + <h3 class=\"code-header\">", + render_stability_since_raw_with_extra( + variant.stable_since(tcx), + variant.const_stability(tcx), + " rightside", + ) + .maybe_display() + )?; + if let clean::VariantItem(ref var) = variant.kind + && let clean::VariantKind::CLike = var.kind + { + write!( + w, + "{}", + display_c_like_variant( + cx, + variant, + var, + index, + should_show_enum_discriminant, + enum_def_id, + ) + )?; + } else { + w.write_str(variant.name.unwrap().as_str())?; + } - let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() }; + let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() }; - if let clean::VariantKind::Tuple(ref s) = variant_data.kind { - write_str(w, format_args!("({})", print_tuple_struct_fields(cx, s))); - } - w.push_str("</h3></section>"); + if let clean::VariantKind::Tuple(ref s) = variant_data.kind { + write!(w, "({})", print_tuple_struct_fields(cx, s))?; + } + w.write_str("</h3></section>")?; - write_str(w, format_args!("{}", document(cx, variant, Some(it), HeadingOffset::H4))); + write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4))?; - let heading_and_fields = match &variant_data.kind { - clean::VariantKind::Struct(s) => { - // If there is no field to display, no need to add the heading. - if s.fields.iter().any(|f| !f.is_doc_hidden()) { - Some(("Fields", &s.fields)) - } else { - None + let heading_and_fields = match &variant_data.kind { + clean::VariantKind::Struct(s) => { + // If there is no field to display, no need to add the heading. + if s.fields.iter().any(|f| !f.is_doc_hidden()) { + Some(("Fields", &s.fields)) + } else { + None + } } - } - clean::VariantKind::Tuple(fields) => { - // Documentation on tuple variant fields is rare, so to reduce noise we only emit - // the section if at least one field is documented. - if fields.iter().any(|f| !f.doc_value().is_empty()) { - Some(("Tuple Fields", fields)) - } else { - None + clean::VariantKind::Tuple(fields) => { + // Documentation on tuple variant fields is rare, so to reduce noise we only emit + // the section if at least one field is documented. + if fields.iter().any(|f| !f.doc_value().is_empty()) { + Some(("Tuple Fields", fields)) + } else { + None + } } - } - clean::VariantKind::CLike => None, - }; + clean::VariantKind::CLike => None, + }; - if let Some((heading, fields)) = heading_and_fields { - let variant_id = - cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); - write_str( - w, - format_args!( + if let Some((heading, fields)) = heading_and_fields { + let variant_id = + cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); + write!( + w, "<div class=\"sub-variant\" id=\"{variant_id}\">\ <h4>{heading}</h4>\ {}", document_non_exhaustive(variant) - ), - ); - for field in fields { - match field.kind { - clean::StrippedItem(box clean::StructFieldItem(_)) => {} - clean::StructFieldItem(ref ty) => { - let id = cx.derive_id(format!( - "variant.{}.field.{}", - variant.name.unwrap(), - field.name.unwrap() - )); - write_str( - w, - format_args!( + )?; + for field in fields { + match field.kind { + clean::StrippedItem(box clean::StructFieldItem(_)) => {} + clean::StructFieldItem(ref ty) => { + let id = cx.derive_id(format!( + "variant.{}.field.{}", + variant.name.unwrap(), + field.name.unwrap() + )); + write!( + w, "<div class=\"sub-variant-field\">\ <span id=\"{id}\" class=\"section-header\">\ <a href=\"#{id}\" class=\"anchor field\">ยง</a>\ <code>{f}: {t}</code>\ - </span>", + </span>\ + {doc}\ + </div>", f = field.name.unwrap(), t = ty.print(cx), - ), - ); - write_str( - w, - format_args!( - "{}</div>", - document(cx, field, Some(variant), HeadingOffset::H5), - ), - ); + doc = document(cx, field, Some(variant), HeadingOffset::H5), + )?; + } + _ => unreachable!(), } - _ => unreachable!(), } + w.write_str("</div>")?; } - w.push_str("</div>"); } - } - write_str(w, format_args!("</div>")); + w.write_str("</div>") + }) } -fn item_macro(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { - wrap_item(w, |w| { - // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`. - if !t.macro_rules { - write_str(w, format_args!("{}", visibility_print_with_space(it, cx))); - } - write_str(w, format_args!("{}", Escape(&t.source))); - }); - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); +fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`. + if !t.macro_rules { + write!(w, "{}", visibility_print_with_space(it, cx))?; + } + write!(w, "{}", Escape(&t.source)) + })?; + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + }) } -fn item_proc_macro( - w: &mut impl fmt::Write, - cx: &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!(buffer, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}") - .unwrap(); - } - MacroKind::Attr => { - write!(buffer, "#[{name}]").unwrap(); - } - MacroKind::Derive => { - write!(buffer, "#[derive({name})]").unwrap(); - if !m.helpers.is_empty() { - buffer - .write_str( +fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + let name = it.name.expect("proc-macros always have names"); + match m.kind { + MacroKind::Bang => { + write!(w, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")?; + } + MacroKind::Attr => { + write!(w, "#[{name}]")?; + } + MacroKind::Derive => { + write!(w, "#[derive({name})]")?; + if !m.helpers.is_empty() { + w.write_str( "\n{\n \ - <span class=\"comment\">// Attributes available to this derive:</span>\n", - ) - .unwrap(); - for attr in &m.helpers { - writeln!(buffer, " #[{attr}]").unwrap(); + <span class=\"comment\">// Attributes available to this derive:</span>\n", + )?; + for attr in &m.helpers { + writeln!(w, " #[{attr}]")?; + } + w.write_str("}\n")?; } - buffer.write_str("}\n").unwrap(); } } - } - }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); + Ok(()) + })?; + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + }) } -fn item_primitive(w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item) { - let def_id = it.item_id.expect_def_id(); - 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)).unwrap(); - } else { - // We handle the "reference" primitive type on its own because we only want to list - // implementations on generic types. - let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&cx.shared, it); - - render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl); - } +fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { + fmt::from_fn(|w| { + 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) { + 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. + let (concrete, synthetic, blanket_impl) = + get_filtered_impls_for_reference(&cx.shared, it); + + render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl); + } + Ok(()) + }) } fn item_constant( - w: &mut String, cx: &Context<'_>, it: &clean::Item, generics: &clean::Generics, ty: &clean::Type, c: &clean::ConstantKind, -) { - wrap_item(w, |w| { - let tcx = cx.tcx(); - render_attributes_in_code(w, it, cx); +) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + let tcx = cx.tcx(); + render_attributes_in_code(w, it, cx); - write_str( - w, - format_args!( + write!( + w, "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), generics = generics.print(cx), typ = ty.print(cx), - where_clause = print_where_clause(generics, cx, 0, Ending::NoNewline) - ), - ); + where_clause = + print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(), + )?; - // FIXME: The code below now prints - // ` = _; // 100i32` - // if the expression is - // `50 + 50` - // which looks just wrong. - // Should we print - // ` = 100i32;` - // instead? - - let value = c.value(tcx); - let is_literal = c.is_literal(tcx); - let expr = c.expr(tcx); - if value.is_some() || is_literal { - write_str(w, format_args!(" = {expr};", expr = Escape(&expr))); - } else { - w.push_str(";"); - } + // FIXME: The code below now prints + // ` = _; // 100i32` + // if the expression is + // `50 + 50` + // which looks just wrong. + // Should we print + // ` = 100i32;` + // instead? + + let value = c.value(tcx); + let is_literal = c.is_literal(tcx); + let expr = c.expr(tcx); + if value.is_some() || is_literal { + write!(w, " = {expr};", expr = Escape(&expr))?; + } else { + w.write_str(";")?; + } - if !is_literal { - if let Some(value) = &value { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = expr.to_lowercase(); + if !is_literal { + if let Some(value) = &value { + let value_lowercase = value.to_lowercase(); + let expr_lowercase = expr.to_lowercase(); - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write_str(w, format_args!(" // {value}", value = Escape(value))); + if value_lowercase != expr_lowercase + && value_lowercase.trim_end_matches("i32") != expr_lowercase + { + write!(w, " // {value}", value = Escape(value))?; + } } } - } - }); + Ok(()) + })?; - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + }) } -fn item_struct(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { - wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); - render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); - }); - - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + render_attributes_in_code(w, it, cx); + write!( + w, + "{}", + render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx) + ) + })?; - item_fields(w, cx, it, &s.fields, s.ctor_kind); + let def_id = it.item_id.expect_def_id(); - let def_id = it.item_id.expect_def_id(); - write_str(w, format_args!("{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))); - write_str(w, format_args!("{}", document_type_layout(cx, def_id))); + write!( + w, + "{}{}{}{}", + document(cx, it, None, HeadingOffset::H2), + item_fields(cx, it, &s.fields, s.ctor_kind), + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id), + ) + }) } fn item_fields( - w: &mut String, cx: &Context<'_>, it: &clean::Item, fields: &[clean::Item], ctor_kind: Option<CtorKind>, -) { - let mut fields = fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if let None | Some(CtorKind::Fn) = ctor_kind { - if fields.peek().is_some() { - let title = format!( - "{}{}", - if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, - document_non_exhaustive_header(it), - ); - write_section_heading(w, &title, "fields", Some("fields"), document_non_exhaustive(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()); - let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField)); - write_str( +) -> impl fmt::Display { + fmt::from_fn(move |w| { + let mut fields = fields + .iter() + .filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable(); + if let None | Some(CtorKind::Fn) = ctor_kind { + if fields.peek().is_some() { + let title = format!( + "{}{}", + if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, + document_non_exhaustive_header(it), + ); + write!( w, - format_args!( + "{}", + write_section_heading( + &title, + "fields", + Some("fields"), + document_non_exhaustive(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()); + let id = + cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField)); + write!( + w, "<span id=\"{id}\" class=\"{item_type} section-header\">\ <a href=\"#{id}\" class=\"anchor field\">ยง</a>\ <code>{field_name}: {ty}</code>\ - </span>", + </span>\ + {doc}", item_type = ItemType::StructField, - ty = ty.print(cx) - ), - ); - write_str(w, format_args!("{}", document(cx, field, Some(it), HeadingOffset::H3))); + ty = ty.print(cx), + doc = document(cx, field, Some(it), HeadingOffset::H3), + )?; + } } } - } + Ok(()) + }) } fn item_static( - w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item, s: &clean::Static, safety: Option<hir::Safety>, -) { - wrap_item(w, |buffer| { - render_attributes_in_code(buffer, it, cx); - write!( - buffer, - "{vis}{safe}static {mutability}{name}: {typ}", - vis = visibility_print_with_space(it, cx), - safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""), - mutability = s.mutability.print_with_space(), - name = it.name.unwrap(), - typ = s.type_.print(cx) - ) - .unwrap(); - }); +) -> impl fmt::Display { + fmt::from_fn(move |w| { + wrap_item(w, |w| { + render_attributes_in_code(w, it, cx); + write!( + w, + "{vis}{safe}static {mutability}{name}: {typ}", + vis = visibility_print_with_space(it, cx), + safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""), + mutability = s.mutability.print_with_space(), + name = it.name.unwrap(), + typ = s.type_.print(cx) + ) + })?; - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) + }) } -fn item_foreign_type(w: &mut impl fmt::Write, cx: &Context<'_>, it: &clean::Item) { - wrap_item(w, |buffer| { - buffer.write_str("extern {\n").unwrap(); - render_attributes_in_code(buffer, it, cx); +fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { + fmt::from_fn(|w| { + wrap_item(w, |w| { + w.write_str("extern {\n")?; + render_attributes_in_code(w, it, cx); + write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),) + })?; + write!( - buffer, - " {}type {};\n}}", - visibility_print_with_space(it, cx), - it.name.unwrap(), + w, + "{}{}", + document(cx, it, None, HeadingOffset::H2), + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All) ) - .unwrap(); - }); - - 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 String, cx: &Context<'_>, it: &clean::Item) { - write_str(w, format_args!("{}", document(cx, it, None, HeadingOffset::H2))); +fn item_keyword(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { + document(cx, it, None, HeadingOffset::H2) } /// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). @@ -2111,18 +2185,14 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { s } -pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display + '_ { +pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display { fmt::from_fn(move |f| match ty { ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)), _ => write!(f, "{ty}.{name}.html"), }) } -fn bounds<'a, 'tcx>( - bounds: &'a [clean::GenericBound], - trait_alias: bool, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display { (!bounds.is_empty()) .then_some(fmt::from_fn(move |f| { let has_lots_of_bounds = bounds.len() > 2; @@ -2140,14 +2210,15 @@ fn bounds<'a, 'tcx>( .maybe_display() } -fn wrap_item<W, F>(w: &mut W, f: F) +fn wrap_item<W, F, T>(w: &mut W, f: F) -> T where W: fmt::Write, - F: FnOnce(&mut W), + F: FnOnce(&mut W) -> T, { write!(w, r#"<pre class="rust item-decl"><code>"#).unwrap(); - f(w); + let res = f(w); write!(w, "</code></pre>").unwrap(); + res } #[derive(PartialEq, Eq)] @@ -2175,10 +2246,9 @@ fn render_implementor( cx: &Context<'_>, implementor: &Impl, trait_: &clean::Item, - w: &mut String, implementor_dups: &FxHashMap<Symbol, (DefId, bool)>, aliases: &[String], -) { +) -> impl fmt::Display { // If there's already another implementor that has the same abridged name, use the // full path, for example in `std::iter::ExactSizeIterator` let use_absolute = match implementor.inner_impl().for_ { @@ -2191,7 +2261,6 @@ fn render_implementor( _ => false, }; render_impl( - w, cx, implementor, trait_, @@ -2205,26 +2274,29 @@ fn render_implementor( show_non_assoc_items: false, toggle_open_by_default: 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 Display + 'a + Captures<'cx> { +fn render_union( + it: &clean::Item, + g: Option<&clean::Generics>, + fields: &[clean::Item], + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |mut f| { write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?; - let where_displayed = g - .map(|g| { - let mut buf = g.print(cx).to_string(); - let where_displayed = print_where_clause_and_check(&mut buf, g, cx); - f.write_str(&buf).unwrap(); - where_displayed - }) - .unwrap_or(false); + let where_displayed = if let Some(generics) = g { + write!(f, "{}", generics.print(cx))?; + if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) { + write!(f, "{where_clause}")?; + true + } else { + false + } + } else { + false + }; // If there wasn't a `where` clause, we add a whitespace. if !where_displayed { @@ -2263,7 +2335,6 @@ fn render_union<'a, 'cx: 'a>( } fn render_struct( - w: &mut String, it: &clean::Item, g: Option<&clean::Generics>, ty: Option<CtorKind>, @@ -2271,33 +2342,35 @@ fn render_struct( tab: &str, structhead: bool, cx: &Context<'_>, -) { - write_str( - w, - format_args!( +) -> impl fmt::Display { + fmt::from_fn(move |w| { + write!( + w, "{}{}{}", visibility_print_with_space(it, cx), if structhead { "struct " } else { "" }, it.name.unwrap() - ), - ); - if let Some(g) = g { - write_str(w, format_args!("{}", g.print(cx))); - } - render_struct_fields( - w, - g, - ty, - fields, - tab, - structhead, - it.has_stripped_entries().unwrap_or(false), - cx, - ) + )?; + if let Some(g) = g { + write!(w, "{}", g.print(cx))?; + } + write!( + w, + "{}", + render_struct_fields( + g, + ty, + fields, + tab, + structhead, + it.has_stripped_entries().unwrap_or(false), + cx, + ) + ) + }) } fn render_struct_fields( - mut w: &mut String, g: Option<&clean::Generics>, ty: Option<CtorKind>, fields: &[clean::Item], @@ -2305,112 +2378,123 @@ fn render_struct_fields( structhead: bool, has_stripped_entries: bool, cx: &Context<'_>, -) { - match ty { - None => { - let where_displayed = - g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false); +) -> impl fmt::Display { + fmt::from_fn(move |w| { + match ty { + None => { + let where_displayed = if let Some(generics) = g + && let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) + { + write!(w, "{where_clause}")?; + true + } else { + false + }; - // If there wasn't a `where` clause, we add a whitespace. - if !where_displayed { - w.push_str(" {"); - } else { - w.push_str("{"); - } - let count_fields = - fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count(); - let has_visible_fields = count_fields > 0; - let toggle = should_hide_fields(count_fields); - if toggle { - toggle_open(&mut w, format_args!("{count_fields} fields")); - } - for field in fields { - if let clean::StructFieldItem(ref ty) = field.kind { - write_str( - w, - format_args!( + // If there wasn't a `where` clause, we add a whitespace. + if !where_displayed { + w.write_str(" {")?; + } else { + w.write_str("{")?; + } + let count_fields = + fields.iter().filter(|f| matches!(f.kind, clean::StructFieldItem(..))).count(); + let has_visible_fields = count_fields > 0; + let toggle = should_hide_fields(count_fields); + if toggle { + toggle_open(&mut *w, format_args!("{count_fields} fields")); + } + for field in fields { + if let clean::StructFieldItem(ref ty) = field.kind { + write!( + w, "\n{tab} {vis}{name}: {ty},", vis = visibility_print_with_space(field, cx), name = field.name.unwrap(), ty = ty.print(cx) - ), - ); + )?; + } } - } - if has_visible_fields { - if has_stripped_entries { - write_str( - w, - format_args!( + if has_visible_fields { + if has_stripped_entries { + write!( + w, "\n{tab} <span class=\"comment\">/* private fields */</span>" - ), - ); + )?; + } + write!(w, "\n{tab}")?; + } else if has_stripped_entries { + write!(w, " <span class=\"comment\">/* private fields */</span> ")?; } - write_str(w, format_args!("\n{tab}")); - } else if has_stripped_entries { - write_str(w, format_args!(" <span class=\"comment\">/* private fields */</span> ")); - } - if toggle { - toggle_close(&mut w); + if toggle { + toggle_close(&mut *w); + } + w.write_str("}")?; } - w.push_str("}"); - } - Some(CtorKind::Fn) => { - w.push_str("("); - if !fields.is_empty() - && fields.iter().all(|field| { - matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..))) - }) - { - write_str(w, format_args!("<span class=\"comment\">/* private fields */</span>")); - } else { - for (i, field) in fields.iter().enumerate() { - if i > 0 { - w.push_str(", "); - } - match field.kind { - clean::StrippedItem(box clean::StructFieldItem(..)) => { - write_str(w, format_args!("_")); + Some(CtorKind::Fn) => { + w.write_str("(")?; + if !fields.is_empty() + && fields.iter().all(|field| { + matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..))) + }) + { + write!(w, "<span class=\"comment\">/* private fields */</span>")?; + } else { + for (i, field) in fields.iter().enumerate() { + if i > 0 { + w.write_str(", ")?; } - clean::StructFieldItem(ref ty) => { - write_str( - w, - format_args!( + match field.kind { + clean::StrippedItem(box clean::StructFieldItem(..)) => { + write!(w, "_")?; + } + clean::StructFieldItem(ref ty) => { + write!( + w, "{}{}", visibility_print_with_space(field, cx), ty.print(cx) - ), - ); + )?; + } + _ => unreachable!(), } - _ => unreachable!(), } } + w.write_str(")")?; + if let Some(g) = g { + write!( + w, + "{}", + print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display() + )?; + } + // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct. + if structhead { + w.write_str(";")?; + } } - w.push_str(")"); - if let Some(g) = g { - write_str(w, format_args!("{}", print_where_clause(g, cx, 0, Ending::NoNewline))); - } - // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct. - if structhead { - w.push_str(";"); - } - } - Some(CtorKind::Const) => { - // Needed for PhantomData. - if let Some(g) = g { - write_str(w, format_args!("{}", print_where_clause(g, cx, 0, Ending::NoNewline))); + Some(CtorKind::Const) => { + // Needed for PhantomData. + if let Some(g) = g { + write!( + w, + "{}", + print_where_clause(g, cx, 0, Ending::NoNewline).maybe_display() + )?; + } + w.write_str(";")?; } - w.push_str(";"); } - } + Ok(()) + }) } fn document_non_exhaustive_header(item: &clean::Item) -> &str { if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } } -fn document_non_exhaustive(item: &clean::Item) -> impl Display + '_ { +fn document_non_exhaustive(item: &clean::Item) -> impl Display { fmt::from_fn(|f| { if item.is_non_exhaustive() { write!( diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index 0f01db5f6bc..a1ee5c8c548 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -2,7 +2,6 @@ use std::fmt; use rinja::Template; use rustc_abi::{Primitive, TagEncoding, Variants}; -use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::layout::LayoutError; @@ -26,10 +25,7 @@ struct TypeLayoutSize { size: u64, } -pub(crate) fn document_type_layout<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - ty_def_id: DefId, -) -> impl fmt::Display + 'a + Captures<'cx> { +pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl fmt::Display { fmt::from_fn(move |f| { if !cx.shared.show_type_layout { return Ok(()); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index a4dec013fc0..b2bbf4614bf 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -153,7 +153,7 @@ fn write_rendered_cross_crate_info( include_sources: bool, ) -> Result<(), Error> { let m = &opt.should_merge; - if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) { + if opt.should_emit_crate() { if include_sources { write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?; } @@ -636,26 +636,22 @@ impl TypeAliasPart { } else { AssocItemLink::Anchor(None) }; - let text = { - let mut buf = String::new(); - super::render_impl( - &mut buf, - cx, - impl_, - type_alias_item, - assoc_link, - RenderMode::Normal, - None, - &[], - ImplRenderingParameters { - show_def_docs: true, - show_default_items: true, - show_non_assoc_items: true, - toggle_open_by_default: true, - }, - ); - buf - }; + let text = super::render_impl( + cx, + impl_, + type_alias_item, + assoc_link, + RenderMode::Normal, + None, + &[], + ImplRenderingParameters { + show_def_docs: true, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, + ) + .to_string(); let type_alias_fqp = (*type_alias_fqp).iter().join("::"); if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) { ret.last_mut() diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 78c86a27632..cbbd4b01d83 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -333,7 +333,7 @@ pub(crate) fn print_src( source_context: &SourceContext<'_>, ) { let mut lines = s.lines().count(); - let line_info = if let SourceContext::Embedded(ref info) = source_context { + let line_info = if let SourceContext::Embedded(info) = source_context { highlight::LineInfo::new_scraped(lines as u32, info.offset as u32) } else { highlight::LineInfo::new(lines as u32) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index acea7828e86..1554c045a32 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -123,7 +123,7 @@ declare namespace rustdoc { * Same as QueryElement, but bindings and typeFilter support strings */ interface ParserQueryElement { - name: string, + name: string|null, id: number|null, fullPath: Array<string>, pathWithoutLast: Array<string>, @@ -131,11 +131,17 @@ declare namespace rustdoc { normalizedPathLast: string, generics: Array<ParserQueryElement>, bindings: Map<string, Array<ParserQueryElement>>, - bindingName: {name: string, generics: ParserQueryElement[]}|null, - typeFilter: string|null, + bindingName: {name: string|null, generics: ParserQueryElement[]}|null, + typeFilter: number|string|null, } /** + * Same as ParserQueryElement, but all fields are optional. + */ + type ParserQueryElementFields = { + [K in keyof ParserQueryElement]?: ParserQueryElement[T] + } + /** * Intermediate parser state. Discarded when parsing is done. */ interface ParserState { @@ -176,10 +182,11 @@ declare namespace rustdoc { name: string, normalizedName: string, word: string, + paramNames: string[], parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), path: string, ty: number, - type?: FunctionSearchType + type: FunctionSearchType | null, } /** @@ -390,7 +397,7 @@ declare namespace rustdoc { */ type RawSearchIndexCrate = { doc: string, - a: Object, + a: { [key: string]: number[] }, n: Array<string>, t: string, D: string, diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index ccbd6811b07..c275127997a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -638,7 +638,6 @@ function getNextElem(query, parserState, elems, isInGenerics) { getFilteredNextElem(query, parserState, generics, isInGenerics); generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { - // @ts-expect-error generics.push(makePrimitiveElement(null, { bindingName: makePrimitiveElement("output"), typeFilter: null, @@ -791,7 +790,7 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { generics: generics.filter(gen => { // Syntactically, bindings are parsed as generics, // but the query engine treats them differently. - if (gen.bindingName !== null) { + if (gen.bindingName !== null && gen.bindingName.name !== null) { if (gen.name !== null) { gen.bindingName.generics.unshift(gen); } @@ -811,8 +810,8 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { /** * - * @param {string} name - * @param {Object=} extra + * @param {string|null} name + * @param {rustdoc.ParserQueryElementFields=} extra * @returns {rustdoc.ParserQueryElement} */ function makePrimitiveElement(name, extra) { @@ -1478,73 +1477,61 @@ class DocSearch { * Special type name IDs for searching by array. * @type {number} */ - // @ts-expect-error this.typeNameIdOfArray = this.buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. * @type {number} */ - // @ts-expect-error this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. * @type {number} */ - // @ts-expect-error this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. * @type {number} */ - // @ts-expect-error this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfHof = this.buildTypeMapIndex("->"); /** * Special type name IDs the output assoc type. * @type {number} */ - // @ts-expect-error this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); /** * Special type name IDs for searching by reference. * @type {number} */ - // @ts-expect-error this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** @@ -1586,7 +1573,6 @@ class DocSearch { /** * @type {Array<rustdoc.Row>} */ - // @ts-expect-error this.searchIndex = this.buildIndex(rawSearchIndex); } @@ -1598,10 +1584,16 @@ class DocSearch { * done more quickly. Two types with the same name but different item kinds * get the same ID. * - * @param {string} name + * @template T extends string + * @overload + * @param {T} name * @param {boolean=} isAssocType - True if this is an assoc type + * @returns {T extends "" ? null : number} + * + * @param {string} name + * @param {boolean=} isAssocType + * @returns {number | null} * - * @returns {number?} */ buildTypeMapIndex(name, isAssocType) { if (name === "" || name === null) { @@ -1909,6 +1901,7 @@ class DocSearch { * Convert raw search index into in-memory search index. * * @param {Map<string, rustdoc.RawSearchIndexCrate>} rawSearchIndex + * @returns {rustdoc.Row[]} */ buildIndex(rawSearchIndex) { /** @@ -2008,6 +2001,7 @@ class DocSearch { return cb; }; + /** @type {rustdoc.Row[]} */ const searchIndex = []; let currentIndex = 0; let id = 0; @@ -2108,8 +2102,6 @@ class DocSearch { // an array of [(Number) item type, // (String) name] const rawPaths = crateCorpus.p; - // an array of [(String) alias name - // [Number] index to items] const aliases = crateCorpus.a; // an array of [(Number) item index, // (String) comma-separated list of function generic param names] @@ -2232,6 +2224,7 @@ class DocSearch { // object defined above. const itemParentIdx = itemParentIdxDecoder.next(); normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, ""); + /** @type {rustdoc.Row} */ const row = { crate, ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" @@ -2274,16 +2267,14 @@ class DocSearch { continue; } - // @ts-expect-error + /** @type{number[]} */ let currentNameAliases; if (currentCrateAliases.has(alias_name)) { currentNameAliases = currentCrateAliases.get(alias_name); } else { currentNameAliases = []; - // @ts-expect-error currentCrateAliases.set(alias_name, currentNameAliases); } - // @ts-expect-error for (const local_alias of aliases[alias_name]) { currentNameAliases.push(local_alias + currentIndex); } @@ -2326,15 +2317,13 @@ class DocSearch { * @param {rustdoc.ParserQueryElement} elem */ function convertTypeFilterOnElem(elem) { - if (elem.typeFilter !== null) { + if (typeof elem.typeFilter === "string") { let typeFilter = elem.typeFilter; if (typeFilter === "const") { typeFilter = "constant"; } - // @ts-expect-error elem.typeFilter = itemTypeFromName(typeFilter); } else { - // @ts-expect-error elem.typeFilter = NO_TYPE_FILTER; } for (const elem2 of elem.generics) { @@ -2407,9 +2396,9 @@ class DocSearch { continue; } if (!foundStopChar) { - let extra = ""; + /** @type String[] */ + let extra = []; if (isLastElemGeneric(query.elems, parserState)) { - // @ts-expect-error extra = [" after ", ">"]; } else if (prevIs(parserState, "\"")) { throw ["Cannot have more than one element if you use quotes"]; @@ -2547,7 +2536,7 @@ class DocSearch { * See `buildTypeMapIndex` for more information. * * @param {rustdoc.QueryElement} elem - * @param {boolean} isAssocType + * @param {boolean=} isAssocType */ const convertNameToId = (elem, isAssocType) => { const loweredName = elem.pathLast.toLowerCase(); @@ -2627,7 +2616,6 @@ class DocSearch { ]; } for (const elem2 of elem.generics) { - // @ts-expect-error convertNameToId(elem2); } elem.bindings = new Map(Array.from(elem.bindings.entries()) @@ -2750,7 +2738,11 @@ class DocSearch { return [displayPath, href, `${exactPath}::${name}`]; }; - // @ts-expect-error + /** + * + * @param {string} path + * @returns {string} + */ function pathSplitter(path) { const tmp = "<span>" + path.replace(/::/g, "::</span><span>"); if (tmp.endsWith("<span>")) { @@ -2763,9 +2755,9 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[rustdoc.ResultObject]} results + * @param {rustdoc.ResultObject[]} results * @param {"sig"|"elems"|"returned"|null} typeInfo - * @returns {[rustdoc.ResultObject]} + * @returns {rustdoc.ResultObject[]} */ const transformResults = (results, typeInfo) => { const duplicates = new Set(); @@ -2840,7 +2832,7 @@ class DocSearch { } let fnInputs = null; let fnOutput = null; - // @ts-expect-error + /** @type {Map<number, number> | null} */ let mgens = null; if (typeInfo !== "elems" && typeInfo !== "returned") { fnInputs = unifyFunctionTypes( @@ -3053,7 +3045,6 @@ class DocSearch { writeFn(nested, result); } return; - // @ts-expect-error } else if (mgens) { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { @@ -3069,7 +3060,7 @@ class DocSearch { name: fnParamNames[-1 - fnType.id], highlighted: !!fnType.highlighted, }, result); - // @ts-expect-error + /** @type{string[]} */ const where = []; onEachBtwn( fnType.generics, @@ -3079,7 +3070,6 @@ class DocSearch { () => pushText({ name: " + ", highlighted: false }, where), ); if (where.length > 0) { - // @ts-expect-error whereClause.set(fnParamNames[-1 - fnType.id], where); } } else { @@ -3181,7 +3171,7 @@ class DocSearch { * @param {rustdoc.Results} results * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {string} preferredCrate - * @returns {Promise<[rustdoc.ResultObject]>} + * @returns {Promise<rustdoc.ResultObject[]>} */ const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; @@ -3337,7 +3327,6 @@ class DocSearch { return 0; }); - // @ts-expect-error return transformResults(result_list, typeInfo); }; |
