summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
commit631cd5845e8de329d0e227aaa707d7ea228b8f8f (patch)
treea1b87c8f8cad01cf18f7c5f57a08f102771ed303 /src/librustdoc/html/render
parentAdding debian version 1.69.0+dfsg1-1. (diff)
downloadrustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.tar.xz
rustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r--src/librustdoc/html/render/context.rs57
-rw-r--r--src/librustdoc/html/render/mod.rs1092
-rw-r--r--src/librustdoc/html/render/print_item.rs806
-rw-r--r--src/librustdoc/html/render/search_index.rs68
-rw-r--r--src/librustdoc/html/render/sidebar.rs558
-rw-r--r--src/librustdoc/html/render/span_map.rs6
6 files changed, 1356 insertions, 1231 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5e4a59562..ac5054ce1 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
- collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
- LinkFromSrc, StylePath,
+ collect_spans_and_sources, scrape_examples_help,
+ sidebar::print_sidebar,
+ sidebar::{sidebar_module_like, Sidebar},
+ AllTypes, LinkFromSrc, StylePath,
};
-
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
@@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources, static_files};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
+use askama::Template;
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
@@ -350,7 +352,7 @@ impl<'tcx> Context<'tcx> {
},
);
- path = href.into_inner().to_string_lossy().to_string();
+ path = href.into_inner().to_string_lossy().into_owned();
if let Some(c) = path.as_bytes().last() && *c != b'/' {
path.push('/');
@@ -600,17 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
- if shared.cache.crate_version.is_some() {
- write!(sidebar, "<h2 class=\"location\">Crate {}</h2>", crate_name)
+
+ let blocks = sidebar_module_like(all.item_sections());
+ let bar = Sidebar {
+ title_prefix: "Crate ",
+ title: crate_name.as_str(),
+ is_crate: false,
+ version: "",
+ blocks: vec![blocks],
+ path: String::new(),
};
- let mut items = Buffer::html();
- sidebar_module_like(&mut items, all.item_sections());
- if !items.is_empty() {
- sidebar.push_str("<div class=\"sidebar-elems\">");
- sidebar.push_buffer(items);
- sidebar.push_str("</div>");
- }
+ bar.render_into(&mut sidebar).unwrap();
let v = layout::render(
&shared.layout,
@@ -649,11 +652,35 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</noscript>\
<link rel=\"stylesheet\" \
href=\"{static_root_path}{settings_css}\">\
- <script defer src=\"{static_root_path}{settings_js}\"></script>",
+ <script defer src=\"{static_root_path}{settings_js}\"></script>\
+ <link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
+ as=\"style\">\
+ <link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
+ as=\"style\">\
+ <link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
+ as=\"style\">",
static_root_path = page.get_static_root_path(),
settings_css = static_files::STATIC_FILES.settings_css,
settings_js = static_files::STATIC_FILES.settings_js,
- )
+ theme_light_css = static_files::STATIC_FILES.theme_light_css,
+ theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
+ theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
+ );
+ // Pre-load all theme CSS files, so that switching feels seamless.
+ //
+ // When loading settings.html as a popover, the equivalent HTML is
+ // generated in main.js.
+ for file in &shared.style_files {
+ if let Ok(theme) = file.basename() {
+ write!(
+ buf,
+ "<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
+ as=\"style\">",
+ root_path = page.static_root_path.unwrap_or(""),
+ suffix = page.resource_suffix,
+ );
+ }
+ }
},
&shared.style_files,
);
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e6a040d02..463184aca 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -30,6 +30,7 @@ mod tests;
mod context;
mod print_item;
+mod sidebar;
mod span_map;
mod write_shared;
@@ -37,7 +38,6 @@ pub(crate) use self::context::*;
pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc};
use std::collections::VecDeque;
-use std::default::Default;
use std::fmt::{self, Write};
use std::fs;
use std::iter::Peekable;
@@ -46,14 +46,14 @@ use std::rc::Rc;
use std::str;
use std::string::ToString;
+use askama::Template;
use rustc_ast_pretty::pprust;
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::CtorKind;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
-use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::{
symbol::{sym, Symbol},
@@ -69,7 +69,7 @@ use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
- href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
+ display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
Buffer, Ending, HrefError, PrintWithSpace,
};
@@ -104,6 +104,7 @@ pub(crate) struct IndexItem {
pub(crate) parent_idx: Option<usize>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
+ pub(crate) deprecation: Option<Deprecation>,
}
/// A type used for the search index.
@@ -407,149 +408,153 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
)
}
-fn document(
- w: &mut Buffer,
- cx: &mut Context<'_>,
- item: &clean::Item,
- parent: Option<&clean::Item>,
+fn document<'a, 'cx: 'a>(
+ cx: &'a mut Context<'cx>,
+ item: &'a clean::Item,
+ parent: Option<&'a clean::Item>,
heading_offset: HeadingOffset,
-) {
+) -> impl fmt::Display + 'a + Captures<'cx> {
if let Some(ref name) = item.name {
info!("Documenting {}", name);
}
- document_item_info(w, cx, item, parent);
- if parent.is_none() {
- document_full_collapsible(w, item, cx, heading_offset);
- } else {
- document_full(w, item, cx, heading_offset);
- }
+
+ display_fn(move |f| {
+ document_item_info(cx, item, parent).render_into(f).unwrap();
+ if parent.is_none() {
+ write!(f, "{}", document_full_collapsible(item, cx, heading_offset))?;
+ } else {
+ write!(f, "{}", document_full(item, cx, heading_offset))?;
+ }
+ Ok(())
+ })
}
/// Render md_text as markdown.
-fn render_markdown(
- w: &mut Buffer,
- cx: &mut Context<'_>,
- md_text: &str,
+fn render_markdown<'a, 'cx: 'a>(
+ cx: &'a mut Context<'cx>,
+ md_text: &'a str,
links: Vec<RenderedLink>,
heading_offset: HeadingOffset,
-) {
- write!(
- w,
- "<div class=\"docblock\">{}</div>",
- Markdown {
- content: md_text,
- links: &links,
- ids: &mut cx.id_map,
- error_codes: cx.shared.codes,
- edition: cx.shared.edition(),
- playground: &cx.shared.playground,
- heading_offset,
- }
- .into_string()
- )
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(move |f| {
+ write!(
+ f,
+ "<div class=\"docblock\">{}</div>",
+ Markdown {
+ content: md_text,
+ links: &links,
+ ids: &mut cx.id_map,
+ error_codes: cx.shared.codes,
+ edition: cx.shared.edition(),
+ playground: &cx.shared.playground,
+ heading_offset,
+ }
+ .into_string()
+ )
+ })
}
/// Writes a documentation block containing only the first paragraph of the documentation. If the
/// docs are longer, a "Read more" link is appended to the end.
-fn document_short(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &mut Context<'_>,
- link: AssocItemLink<'_>,
- parent: &clean::Item,
+fn document_short<'a, 'cx: 'a>(
+ item: &'a clean::Item,
+ cx: &'a mut Context<'cx>,
+ link: AssocItemLink<'a>,
+ parent: &'a clean::Item,
show_def_docs: bool,
-) {
- document_item_info(w, cx, item, Some(parent));
- if !show_def_docs {
- return;
- }
- if let Some(s) = item.doc_value() {
- let (mut summary_html, has_more_content) =
- MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(move |f| {
+ document_item_info(cx, item, Some(parent)).render_into(f).unwrap();
+ if !show_def_docs {
+ return Ok(());
+ }
+ if let Some(s) = item.doc_value() {
+ let (mut summary_html, has_more_content) =
+ MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
- if has_more_content {
- let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
+ if has_more_content {
+ let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
- if let Some(idx) = summary_html.rfind("</p>") {
- summary_html.insert_str(idx, &link);
- } else {
- summary_html.push_str(&link);
+ if let Some(idx) = summary_html.rfind("</p>") {
+ summary_html.insert_str(idx, &link);
+ } else {
+ summary_html.push_str(&link);
+ }
}
- }
- write!(w, "<div class='docblock'>{}</div>", summary_html,);
- }
+ write!(f, "<div class='docblock'>{}</div>", summary_html)?;
+ }
+ Ok(())
+ })
}
-fn document_full_collapsible(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &mut Context<'_>,
+fn document_full_collapsible<'a, 'cx: 'a>(
+ item: &'a clean::Item,
+ cx: &'a mut Context<'cx>,
heading_offset: HeadingOffset,
-) {
- document_full_inner(w, item, cx, true, heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ document_full_inner(item, cx, true, heading_offset)
}
-fn document_full(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &mut Context<'_>,
+fn document_full<'a, 'cx: 'a>(
+ item: &'a clean::Item,
+ cx: &'a mut Context<'cx>,
heading_offset: HeadingOffset,
-) {
- document_full_inner(w, item, cx, false, heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ document_full_inner(item, cx, false, heading_offset)
}
-fn document_full_inner(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &mut Context<'_>,
+fn document_full_inner<'a, 'cx: 'a>(
+ item: &'a clean::Item,
+ cx: &'a mut Context<'cx>,
is_collapsible: bool,
heading_offset: HeadingOffset,
-) {
- if let Some(s) = item.collapsed_doc_value() {
- debug!("Doc block: =====\n{}\n=====", s);
- if is_collapsible {
- w.write_str(
- "<details class=\"toggle top-doc\" open>\
- <summary class=\"hideme\">\
- <span>Expand description</span>\
- </summary>",
- );
- render_markdown(w, cx, &s, item.links(cx), heading_offset);
- w.write_str("</details>");
- } else {
- render_markdown(w, cx, &s, item.links(cx), heading_offset);
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(move |f| {
+ if let Some(s) = item.collapsed_doc_value() {
+ debug!("Doc block: =====\n{}\n=====", s);
+ if is_collapsible {
+ write!(
+ f,
+ "<details class=\"toggle top-doc\" open>\
+ <summary class=\"hideme\">\
+ <span>Expand description</span>\
+ </summary>{}</details>",
+ render_markdown(cx, &s, item.links(cx), heading_offset)
+ )?;
+ } else {
+ write!(f, "{}", render_markdown(cx, &s, item.links(cx), heading_offset))?;
+ }
}
- }
- let kind = match &*item.kind {
- clean::ItemKind::StrippedItem(box kind) | kind => kind,
- };
+ let kind = match &*item.kind {
+ clean::ItemKind::StrippedItem(box kind) | kind => kind,
+ };
- if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
- render_call_locations(w, cx, item);
- }
+ if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind {
+ render_call_locations(f, cx, item);
+ }
+ Ok(())
+ })
}
+#[derive(Template)]
+#[template(path = "item_info.html")]
+struct ItemInfo {
+ items: Vec<ShortItemInfo>,
+}
/// Add extra information about an item such as:
///
/// * Stability
/// * Deprecated
/// * Required features (through the `doc_cfg` feature)
fn document_item_info(
- w: &mut Buffer,
cx: &mut Context<'_>,
item: &clean::Item,
parent: Option<&clean::Item>,
-) {
- let item_infos = short_item_info(item, cx, parent);
- if !item_infos.is_empty() {
- w.write_str("<span class=\"item-info\">");
- for info in item_infos {
- w.write_str(&info);
- }
- w.write_str("</span>");
- }
+) -> ItemInfo {
+ let items = short_item_info(item, cx, parent);
+ ItemInfo { items }
}
fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
@@ -567,7 +572,25 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin
cfg
);
- Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
+ Some(cfg?.render_long_html())
+}
+
+#[derive(Template)]
+#[template(path = "short_item_info.html")]
+enum ShortItemInfo {
+ /// A message describing the deprecation of this item
+ Deprecation {
+ message: String,
+ },
+ /// The feature corresponding to an unstable item, and optionally
+ /// a tracking issue URL and number.
+ Unstable {
+ feature: String,
+ tracking: Option<(String, u32)>,
+ },
+ Portability {
+ message: String,
+ },
}
/// Render the stability, deprecation and portability information that is displayed at the top of
@@ -576,7 +599,7 @@ fn short_item_info(
item: &clean::Item,
cx: &mut Context<'_>,
parent: Option<&clean::Item>,
-) -> Vec<String> {
+) -> Vec<ShortItemInfo> {
let mut extra_info = vec![];
if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
@@ -602,15 +625,10 @@ fn short_item_info(
if let Some(note) = note {
let note = note.as_str();
let html = MarkdownItemInfo(note, &mut cx.id_map);
- message.push_str(&format!(": {}", html.into_string()));
- }
- extra_info.push(format!(
- "<div class=\"stab deprecated\">\
- <span class=\"emoji\">đź‘Ž</span>\
- <span>{}</span>\
- </div>",
- message,
- ));
+ message.push_str(": ");
+ message.push_str(&html.into_string());
+ }
+ extra_info.push(ShortItemInfo::Deprecation { message });
}
// Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
@@ -621,26 +639,17 @@ fn short_item_info(
.filter(|stab| stab.feature != sym::rustc_private)
.map(|stab| (stab.level, stab.feature))
{
- let mut message = "<span class=\"emoji\">🔬</span>\
- <span>This is a nightly-only experimental API."
- .to_owned();
-
- let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
- if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
- feature.push_str(&format!(
- "&nbsp;<a href=\"{url}{issue}\">#{issue}</a>",
- url = url,
- issue = issue
- ));
- }
-
- message.push_str(&format!(" ({})</span>", feature));
-
- extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
+ let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
+ {
+ Some((url.clone(), issue.get()))
+ } else {
+ None
+ };
+ extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
}
- if let Some(portability) = portability(item, parent) {
- extra_info.push(portability);
+ if let Some(message) = portability(item, parent) {
+ extra_info.push(ShortItemInfo::Portability { message });
}
extra_info
@@ -650,7 +659,7 @@ fn short_item_info(
// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
pub(crate) fn render_impls(
cx: &mut Context<'_>,
- w: &mut Buffer,
+ mut w: impl Write,
impls: &[&Impl],
containing_item: &clean::Item,
toggle_open_by_default: bool,
@@ -662,7 +671,7 @@ pub(crate) fn render_impls(
let did = i.trait_did().unwrap();
let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx);
let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
- let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
+ let mut buffer = Buffer::new();
render_impl(
&mut buffer,
cx,
@@ -683,7 +692,7 @@ pub(crate) fn render_impls(
})
.collect::<Vec<_>>();
rendered_impls.sort();
- w.write_str(&rendered_impls.join(""));
+ w.write_str(&rendered_impls.join("")).unwrap();
}
/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
@@ -839,7 +848,7 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
- render_attributes_in_pre(w, meth, indent_str);
+ write!(w, "{}", render_attributes_in_pre(meth, indent_str));
(4, indent_str, Ending::NoNewline)
} else {
render_attributes_in_code(w, meth);
@@ -1035,10 +1044,16 @@ fn attributes(it: &clean::Item) -> Vec<String> {
// When an attribute is rendered inside a `<pre>` tag, it is formatted using
// a whitespace prefix and newline.
-fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) {
- for a in attributes(it) {
- writeln!(w, "{}{}", prefix, a);
- }
+fn render_attributes_in_pre<'a>(
+ it: &'a clean::Item,
+ prefix: &'a str,
+) -> impl fmt::Display + Captures<'a> {
+ crate::html::format::display_fn(move |f| {
+ for a in attributes(it) {
+ writeln!(f, "{}{}", prefix, a)?;
+ }
+ Ok(())
+ })
}
// When an attribute is rendered inside a <code> tag, it is formatted using
@@ -1064,61 +1079,68 @@ impl<'a> AssocItemLink<'a> {
}
}
-fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
+fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) {
write!(
w,
"<h2 id=\"{id}\" class=\"small-section-header\">\
{title}\
<a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
- );
+ )
+ .unwrap();
}
pub(crate) fn render_all_impls(
- w: &mut Buffer,
+ mut w: impl Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
concrete: &[&Impl],
synthetic: &[&Impl],
blanket_impl: &[&Impl],
) {
- let mut impls = Buffer::empty_from(w);
+ let mut impls = Buffer::html();
render_impls(cx, &mut impls, concrete, containing_item, true);
let impls = impls.into_inner();
if !impls.is_empty() {
- write_impl_section_heading(w, "Trait Implementations", "trait-implementations");
- write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls);
+ write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations");
+ write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls).unwrap();
}
if !synthetic.is_empty() {
- write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
- w.write_str("<div id=\"synthetic-implementations-list\">");
- render_impls(cx, w, synthetic, containing_item, false);
- w.write_str("</div>");
+ write_impl_section_heading(
+ &mut w,
+ "Auto Trait Implementations",
+ "synthetic-implementations",
+ );
+ w.write_str("<div id=\"synthetic-implementations-list\">").unwrap();
+ render_impls(cx, &mut w, synthetic, containing_item, false);
+ w.write_str("</div>").unwrap();
}
if !blanket_impl.is_empty() {
- write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
- w.write_str("<div id=\"blanket-implementations-list\">");
- render_impls(cx, w, blanket_impl, containing_item, false);
- w.write_str("</div>");
+ write_impl_section_heading(&mut w, "Blanket Implementations", "blanket-implementations");
+ w.write_str("<div id=\"blanket-implementations-list\">").unwrap();
+ render_impls(cx, &mut w, blanket_impl, containing_item, false);
+ w.write_str("</div>").unwrap();
}
}
-fn render_assoc_items(
- w: &mut Buffer,
- cx: &mut Context<'_>,
- containing_item: &clean::Item,
+fn render_assoc_items<'a, 'cx: 'a>(
+ cx: &'a mut Context<'cx>,
+ containing_item: &'a clean::Item,
it: DefId,
- what: AssocItemRender<'_>,
-) {
+ what: AssocItemRender<'a>,
+) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
derefs.insert(it);
- render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
+ display_fn(move |f| {
+ render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+ Ok(())
+ })
}
fn render_assoc_items_inner(
- w: &mut Buffer,
+ mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
it: DefId,
@@ -1131,7 +1153,7 @@ fn render_assoc_items_inner(
let Some(v) = cache.impls.get(&it) else { return };
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
- let mut tmp_buf = Buffer::empty_from(w);
+ let mut tmp_buf = Buffer::html();
let (render_mode, id) = match what {
AssocItemRender::All => {
write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
@@ -1155,7 +1177,7 @@ fn render_assoc_items_inner(
(RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
}
};
- let mut impls_buf = Buffer::empty_from(w);
+ let mut impls_buf = Buffer::html();
for i in &non_trait {
render_impl(
&mut impls_buf,
@@ -1175,10 +1197,10 @@ fn render_assoc_items_inner(
);
}
if !impls_buf.is_empty() {
- w.push_buffer(tmp_buf);
- write!(w, "<div id=\"{}\">", id);
- w.push_buffer(impls_buf);
- w.write_str("</div>");
+ write!(w, "{}", tmp_buf.into_inner()).unwrap();
+ write!(w, "<div id=\"{}\">", id).unwrap();
+ write!(w, "{}", impls_buf.into_inner()).unwrap();
+ w.write_str("</div>").unwrap();
}
}
@@ -1188,7 +1210,7 @@ fn render_assoc_items_inner(
if let Some(impl_) = deref_impl {
let has_deref_mut =
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
- render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs);
+ render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
}
// If we were already one level into rendering deref methods, we don't want to render
@@ -1207,7 +1229,7 @@ fn render_assoc_items_inner(
}
fn render_deref_methods(
- w: &mut Buffer,
+ mut w: impl Write,
cx: &mut Context<'_>,
impl_: &Impl,
container_item: &clean::Item,
@@ -1239,10 +1261,10 @@ fn render_deref_methods(
return;
}
}
- render_assoc_items_inner(w, cx, container_item, did, what, derefs);
+ render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
} else if let Some(prim) = target.primitive_type() {
if let Some(&did) = cache.primitive_locations.get(&prim) {
- render_assoc_items_inner(w, cx, container_item, did, what, derefs);
+ render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
}
}
}
@@ -1291,7 +1313,7 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O
if let Some(impls) = cx.cache().impls.get(&did) {
for i in impls {
let impl_ = i.inner_impl();
- if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
// Two different types might have the same did,
// without actually being the same.
continue;
@@ -1327,7 +1349,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
for i in impls {
let impl_ = i.inner_impl();
- if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) {
// Two different types might have the same did,
// without actually being the same.
continue;
@@ -1472,37 +1494,45 @@ fn render_impl(
// We need the stability of the item from the trait
// because impls can't have a stability.
if item.doc_value().is_some() {
- document_item_info(&mut info_buffer, cx, it, Some(parent));
- document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
+ document_item_info(cx, it, Some(parent))
+ .render_into(&mut info_buffer)
+ .unwrap();
+ write!(
+ &mut doc_buffer,
+ "{}",
+ document_full(item, cx, HeadingOffset::H5)
+ );
short_documented = false;
} else {
// In case the item isn't documented,
// provide short documentation from the trait.
- document_short(
+ write!(
&mut doc_buffer,
- it,
- cx,
- link,
- parent,
- rendering_params.show_def_docs,
+ "{}",
+ document_short(
+ it,
+ cx,
+ link,
+ parent,
+ rendering_params.show_def_docs,
+ )
);
}
}
} else {
- document_item_info(&mut info_buffer, cx, item, Some(parent));
+ document_item_info(cx, item, Some(parent))
+ .render_into(&mut info_buffer)
+ .unwrap();
if rendering_params.show_def_docs {
- document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
+ write!(&mut doc_buffer, "{}", document_full(item, cx, HeadingOffset::H5));
short_documented = false;
}
}
} else {
- document_short(
+ write!(
&mut doc_buffer,
- item,
- cx,
- link,
- parent,
- rendering_params.show_def_docs,
+ "{}",
+ document_short(item, cx, link, parent, rendering_params.show_def_docs,)
);
}
}
@@ -1862,161 +1892,17 @@ pub(crate) fn render_impl_summary(
let is_trait = inner_impl.trait_.is_some();
if is_trait {
if let Some(portability) = portability(&i.impl_item, Some(parent)) {
- write!(w, "<span class=\"item-info\">{}</span>", portability);
+ write!(
+ w,
+ "<span class=\"item-info\"><div class=\"stab portability\">{}</div></span>",
+ portability
+ );
}
}
w.write_str("</section>");
}
-fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
- if it.is_struct()
- || it.is_trait()
- || it.is_primitive()
- || it.is_union()
- || it.is_enum()
- || it.is_mod()
- || it.is_typedef()
- {
- write!(
- buffer,
- "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
- match *it.kind {
- clean::ModuleItem(..) =>
- if it.is_crate() {
- "Crate "
- } else {
- "Module "
- },
- _ => "",
- },
- it.name.as_ref().unwrap()
- );
- }
-
- buffer.write_str("<div class=\"sidebar-elems\">");
- if it.is_crate() {
- write!(buffer, "<ul class=\"block\">");
- if let Some(ref version) = cx.cache().crate_version {
- write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
- }
- write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
- buffer.write_str("</ul>");
- }
-
- match *it.kind {
- clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
- clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
- clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
- clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
- clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
- clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
- clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
- clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
- _ => {}
- }
-
- // The sidebar is designed to display sibling functions, modules and
- // other miscellaneous information. since there are lots of sibling
- // items (and that causes quadratic growth in large modules),
- // we refactor common parts into a shared JavaScript file per module.
- // still, we don't move everything into JS because we want to preserve
- // as much HTML as possible in order to allow non-JS-enabled browsers
- // to navigate the documentation (though slightly inefficiently).
-
- if !it.is_mod() {
- let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
-
- write!(buffer, "<h2><a href=\"index.html\">In {}</a></h2>", path);
- }
-
- // Closes sidebar-elems div.
- buffer.write_str("</div>");
-}
-
-fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
- if used_links.insert(url.clone()) {
- return url;
- }
- let mut add = 1;
- while !used_links.insert(format!("{}-{}", url, add)) {
- add += 1;
- }
- format!("{}-{}", url, add)
-}
-
-struct SidebarLink {
- name: Symbol,
- url: String,
-}
-
-impl fmt::Display for SidebarLink {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name)
- }
-}
-
-impl PartialEq for SidebarLink {
- fn eq(&self, other: &Self) -> bool {
- self.url == other.url
- }
-}
-
-impl Eq for SidebarLink {}
-
-impl PartialOrd for SidebarLink {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for SidebarLink {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.url.cmp(&other.url)
- }
-}
-
-fn get_methods(
- i: &clean::Impl,
- for_deref: bool,
- used_links: &mut FxHashSet<String>,
- deref_mut: bool,
- tcx: TyCtxt<'_>,
-) -> Vec<SidebarLink> {
- i.items
- .iter()
- .filter_map(|item| match item.name {
- Some(name) if !name.is_empty() && item.is_method() => {
- if !for_deref || should_render_item(item, deref_mut, tcx) {
- Some(SidebarLink {
- name,
- url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
- })
- } else {
- None
- }
- }
- _ => None,
- })
- .collect::<Vec<_>>()
-}
-
-fn get_associated_constants(
- i: &clean::Impl,
- used_links: &mut FxHashSet<String>,
-) -> Vec<SidebarLink> {
- i.items
- .iter()
- .filter_map(|item| match item.name {
- Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
- name,
- url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
- }),
- _ => None,
- })
- .collect::<Vec<_>>()
-}
-
pub(crate) fn small_url_encode(s: String) -> String {
// These characters don't need to be escaped in a URI.
// See https://url.spec.whatwg.org/#query-percent-encode-set
@@ -2082,232 +1968,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
}
}
-pub(crate) fn sidebar_render_assoc_items(
- cx: &Context<'_>,
- out: &mut Buffer,
- id_map: &mut IdMap,
- concrete: Vec<&Impl>,
- synthetic: Vec<&Impl>,
- blanket_impl: Vec<&Impl>,
-) {
- let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
- let mut links = FxHashSet::default();
-
- let mut ret = impls
- .iter()
- .filter_map(|it| {
- let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
- let i_display = format!("{:#}", trait_.print(cx));
- let out = Escape(&i_display);
- let prefix = match it.inner_impl().polarity {
- ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
- ty::ImplPolarity::Negative => "!",
- };
- let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
- if links.insert(generated.clone()) { Some(generated) } else { None }
- })
- .collect::<Vec<String>>();
- ret.sort();
- ret
- };
-
- let concrete_format = format_impls(concrete, id_map);
- let synthetic_format = format_impls(synthetic, id_map);
- let blanket_format = format_impls(blanket_impl, id_map);
-
- if !concrete_format.is_empty() {
- print_sidebar_block(
- out,
- "trait-implementations",
- "Trait Implementations",
- concrete_format.iter(),
- );
- }
-
- if !synthetic_format.is_empty() {
- print_sidebar_block(
- out,
- "synthetic-implementations",
- "Auto Trait Implementations",
- synthetic_format.iter(),
- );
- }
-
- if !blanket_format.is_empty() {
- print_sidebar_block(
- out,
- "blanket-implementations",
- "Blanket Implementations",
- blanket_format.iter(),
- );
- }
-}
-
-fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
- let did = it.item_id.expect_def_id();
- let cache = cx.cache();
-
- if let Some(v) = cache.impls.get(&did) {
- let mut used_links = FxHashSet::default();
- let mut id_map = IdMap::new();
-
- {
- let used_links_bor = &mut used_links;
- let mut assoc_consts = v
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
- .collect::<Vec<_>>();
- if !assoc_consts.is_empty() {
- // We want links' order to be reproducible so we don't use unstable sort.
- assoc_consts.sort();
-
- print_sidebar_block(
- out,
- "implementations",
- "Associated Constants",
- assoc_consts.iter(),
- );
- }
- let mut methods = v
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
- .collect::<Vec<_>>();
- if !methods.is_empty() {
- // We want links' order to be reproducible so we don't use unstable sort.
- methods.sort();
-
- print_sidebar_block(out, "implementations", "Methods", methods.iter());
- }
- }
-
- if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
- if let Some(impl_) =
- v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
- {
- let mut derefs = DefIdSet::default();
- derefs.insert(did);
- sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
- }
-
- let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
- v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
- concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
-
- sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
- }
- }
-}
-
-fn sidebar_deref_methods(
- cx: &Context<'_>,
- out: &mut Buffer,
- impl_: &Impl,
- v: &[Impl],
- derefs: &mut DefIdSet,
- used_links: &mut FxHashSet<String>,
-) {
- let c = cx.cache();
-
- debug!("found Deref: {:?}", impl_);
- if let Some((target, real_target)) =
- impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
- clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
- _ => (&t.type_, &t.type_),
- }),
- _ => None,
- })
- {
- debug!("found target, real_target: {:?} {:?}", target, real_target);
- if let Some(did) = target.def_id(c) &&
- let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
- // `impl Deref<Target = S> for S`
- (did == type_did || !derefs.insert(did))
- {
- // Avoid infinite cycles
- return;
- }
- let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
- let inner_impl = target
- .def_id(c)
- .or_else(|| {
- target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
- })
- .and_then(|did| c.impls.get(&did));
- if let Some(impls) = inner_impl {
- debug!("found inner_impl: {:?}", impls);
- let mut ret = impls
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
- .collect::<Vec<_>>();
- if !ret.is_empty() {
- let id = if let Some(target_def_id) = real_target.def_id(c) {
- cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
- } else {
- "deref-methods"
- };
- let title = format!(
- "Methods from {}&lt;Target={}&gt;",
- Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
- Escape(&format!("{:#}", real_target.print(cx))),
- );
- // We want links' order to be reproducible so we don't use unstable sort.
- ret.sort();
- print_sidebar_block(out, id, &title, ret.iter());
- }
- }
-
- // Recurse into any further impls that might exist for `target`
- if let Some(target_did) = target.def_id(c) &&
- let Some(target_impls) = c.impls.get(&target_did) &&
- let Some(target_deref_impl) = target_impls.iter().find(|i| {
- i.inner_impl()
- .trait_
- .as_ref()
- .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
- .unwrap_or(false)
- })
- {
- sidebar_deref_methods(
- cx,
- out,
- target_deref_impl,
- target_impls,
- derefs,
- used_links,
- );
- }
- }
-}
-
-fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
- let mut sidebar = Buffer::new();
- let fields = get_struct_fields_name(&s.fields);
-
- if !fields.is_empty() {
- match s.ctor_kind {
- None => {
- print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
- }
- Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
- Some(CtorKind::Const) => {}
- }
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
match trait_ {
Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
@@ -2328,131 +1988,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String
}
}
-fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
- write!(buf, "<h3><a href=\"#{}\">{}</a></h3>", id, title);
-}
-
-fn print_sidebar_block(
- buf: &mut Buffer,
- id: &str,
- title: &str,
- items: impl Iterator<Item = impl fmt::Display>,
-) {
- print_sidebar_title(buf, id, title);
- buf.push_str("<ul class=\"block\">");
- for item in items {
- write!(buf, "<li>{}</li>", item);
- }
- buf.push_str("</ul>");
-}
-
-fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
- buf.write_str("<section>");
-
- fn print_sidebar_section(
- out: &mut Buffer,
- items: &[clean::Item],
- id: &str,
- title: &str,
- filter: impl Fn(&clean::Item) -> bool,
- mapper: impl Fn(&str) -> String,
- ) {
- let mut items: Vec<&str> = items
- .iter()
- .filter_map(|m| match m.name {
- Some(ref name) if filter(m) => Some(name.as_str()),
- _ => None,
- })
- .collect::<Vec<_>>();
-
- if !items.is_empty() {
- items.sort_unstable();
- print_sidebar_block(out, id, title, items.into_iter().map(mapper));
- }
- }
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-associated-types",
- "Required Associated Types",
- |m| m.is_ty_associated_type(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-associated-types",
- "Provided Associated Types",
- |m| m.is_associated_type(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-associated-consts",
- "Required Associated Constants",
- |m| m.is_ty_associated_const(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-associated-consts",
- "Provided Associated Constants",
- |m| m.is_associated_const(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-methods",
- "Required Methods",
- |m| m.is_ty_method(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-methods",
- "Provided Methods",
- |m| m.is_method(),
- |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
- );
-
- if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
- let mut res = implementors
- .iter()
- .filter(|i| !i.is_on_local_type(cx))
- .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
- .collect::<Vec<_>>();
-
- if !res.is_empty() {
- res.sort();
- print_sidebar_block(
- buf,
- "foreign-impls",
- "Implementations on Foreign Types",
- res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))),
- );
- }
- }
-
- sidebar_assoc_items(cx, buf, it);
-
- print_sidebar_title(buf, "implementors", "Implementors");
- if t.is_auto(cx.tcx()) {
- print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
- }
-
- buf.push_str("</section>")
-}
-
/// Returns the list of implementations for the primitive reference type, filtering out any
/// implementations that are on concrete or partially generic types, only keeping implementations
/// of the form `impl<T> Trait for &T`.
@@ -2483,89 +2018,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>(
(concrete, synthetic, blanket_impl)
}
-fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
-
- if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
- sidebar_assoc_items(cx, &mut sidebar, it);
- } else {
- let shared = Rc::clone(&cx.shared);
- let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
-
- sidebar_render_assoc_items(
- cx,
- &mut sidebar,
- &mut IdMap::new(),
- concrete,
- synthetic,
- blanket_impl,
- );
- }
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
-fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
-fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
- let mut fields = fields
- .iter()
- .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
- .filter_map(|f| {
- f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
- })
- .collect::<Vec<_>>();
- fields.sort();
- fields
-}
-
-fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
- let mut sidebar = Buffer::new();
- let fields = get_struct_fields_name(&u.fields);
-
- if !fields.is_empty() {
- print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
-fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
- let mut sidebar = Buffer::new();
-
- let mut variants = e
- .variants()
- .filter_map(|v| {
- v.name
- .as_ref()
- .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name))
- })
- .collect::<Vec<_>>();
- if !variants.is_empty() {
- variants.sort_unstable();
- print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum ItemSection {
Reexports,
@@ -2719,54 +2171,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
}
}
-pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet<ItemSection>) {
- use std::fmt::Write as _;
-
- let mut sidebar = String::new();
-
- for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
- let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name());
- }
-
- if !sidebar.is_empty() {
- write!(
- buf,
- "<section>\
- <ul class=\"block\">{}</ul>\
- </section>",
- sidebar
- );
- }
-}
-
-fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
- let item_sections_in_use: FxHashSet<_> = items
- .iter()
- .filter(|it| {
- !it.is_stripped()
- && it
- .name
- .or_else(|| {
- if let clean::ImportItem(ref i) = *it.kind &&
- let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
- })
- .is_some()
- })
- .map(|it| item_ty_to_section(it.type_()))
- .collect();
-
- sidebar_module_like(buf, item_sections_in_use);
-}
-
-fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "<section>{}</section>", sidebar.into_inner());
- }
-}
-
/// Returns a list of all paths used in the type.
/// This is used to help deduplicate imported impls
/// for reexported types. If any of the contained
@@ -2825,7 +2229,7 @@ const MAX_FULL_EXAMPLES: usize = 5;
const NUM_VISIBLE_LINES: usize = 10;
/// Generates the HTML for example call locations generated via the --scrape-examples flag.
-fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) {
+fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &clean::Item) {
let tcx = cx.tcx();
let def_id = item.item_id.expect_def_id();
let key = tcx.def_path_hash(def_id);
@@ -2834,7 +2238,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
// Generate a unique ID so users can link to this section for a given method
let id = cx.id_map.derive("scraped-examples");
write!(
- w,
+ &mut w,
"<div class=\"docblock scraped-example-list\">\
<span></span>\
<h5 id=\"{id}\">\
@@ -2843,7 +2247,8 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
</h5>",
root_path = cx.root_path(),
id = id
- );
+ )
+ .unwrap();
// Create a URL to a particular location in a reverse-dependency's source file
let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) {
@@ -2861,7 +2266,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
};
// Generate the HTML for a single example, being the title and code block
- let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool {
+ let write_example = |mut w: &mut W, (path, call_data): (&PathBuf, &CallData)| -> bool {
let contents = match fs::read_to_string(&path) {
Ok(contents) => contents,
Err(err) => {
@@ -2909,7 +2314,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
write!(
- w,
+ &mut w,
"<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\
<div class=\"scraped-example-title\">\
{name} (<a href=\"{url}\">{title}</a>)\
@@ -2922,10 +2327,12 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
// The locations are encoded as a data attribute, so they can be read
// later by the JS for interactions.
locations = Escape(&locations_encoded)
- );
+ )
+ .unwrap();
if line_ranges.len() > 1 {
- write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#);
+ write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#)
+ .unwrap();
}
// Look for the example file in the source map if it exists, otherwise return a dummy span
@@ -2952,7 +2359,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
decoration_info.insert("highlight", byte_ranges);
sources::print_src(
- w,
+ &mut w,
contents_subset,
file_span,
cx,
@@ -2960,7 +2367,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
highlight::DecorationInfo(decoration_info),
sources::SourceContext::Embedded { offset: line_min, needs_expansion },
);
- write!(w, "</div></div>");
+ write!(w, "</div></div>").unwrap();
true
};
@@ -2994,7 +2401,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
// An example may fail to write if its source can't be read for some reason, so this method
// continues iterating until a write succeeds
- let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| {
+ let write_and_skip_failure = |w: &mut W, it: &mut Peekable<_>| {
while let Some(example) = it.next() {
if write_example(&mut *w, example) {
break;
@@ -3003,7 +2410,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
};
// Write just one example that's visible by default in the method's description.
- write_and_skip_failure(w, &mut it);
+ write_and_skip_failure(&mut w, &mut it);
// Then add the remaining examples in a hidden section.
if it.peek().is_some() {
@@ -3016,17 +2423,19 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
<div class=\"hide-more\">Hide additional examples</div>\
<div class=\"more-scraped-examples\">\
<div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
- );
+ )
+ .unwrap();
// Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
// make the page arbitrarily huge!
for _ in 0..MAX_FULL_EXAMPLES {
- write_and_skip_failure(w, &mut it);
+ write_and_skip_failure(&mut w, &mut it);
}
// For the remaining examples, generate a <ul> containing links to the source files.
if it.peek().is_some() {
- write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#);
+ write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#)
+ .unwrap();
it.for_each(|(_, call_data)| {
let (url, _) = link_to_loc(call_data, &call_data.locations[0]);
write!(
@@ -3034,13 +2443,14 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
r#"<li><a href="{url}">{name}</a></li>"#,
url = url,
name = call_data.display_name
- );
+ )
+ .unwrap();
});
- write!(w, "</ul></div>");
+ write!(w, "</ul></div>").unwrap();
}
- write!(w, "</div></details>");
+ write!(w, "</div></details>").unwrap();
}
- write!(w, "</div>");
+ write!(w, "</div>").unwrap();
}
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 2869a3961..9a968e48b 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1,5 +1,6 @@
use clean::AttributesExt;
+use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
@@ -28,8 +29,8 @@ use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
- join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
- visibility_print_with_space, Buffer, Ending, PrintWithSpace,
+ display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space,
+ print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace,
};
use crate::html::layout::Page;
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
@@ -201,7 +202,7 @@ fn should_hide_fields(n_fields: usize) -> bool {
n_fields > 12
}
-fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
+fn toggle_open(mut w: impl fmt::Write, text: impl fmt::Display) {
write!(
w,
"<details class=\"toggle type-contents-toggle\">\
@@ -209,15 +210,16 @@ fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
<span>Show {}</span>\
</summary>",
text
- );
+ )
+ .unwrap();
}
-fn toggle_close(w: &mut Buffer) {
- w.write_str("</details>");
+fn toggle_close(mut w: impl fmt::Write) {
+ w.write_str("</details>").unwrap();
}
fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) {
- document(w, cx, item, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>();
@@ -367,7 +369,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
..myitem.clone()
};
- let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
+ let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()).to_string());
stab_tags
} else {
None
@@ -461,41 +463,62 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
/// Render the stability, deprecation and portability tags that are displayed in the item's summary
/// at the module level.
-fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
- let mut tags = String::new();
-
- fn tag_html(class: &str, title: &str, contents: &str) -> String {
- format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
- }
-
- // The trailing space after each tag is to space it properly against the rest of the docs.
- if let Some(depr) = &item.deprecation(tcx) {
- let mut message = "Deprecated";
- if !stability::deprecation_in_effect(depr) {
- message = "Deprecation planned";
+fn extra_info_tags<'a, 'tcx: 'a>(
+ item: &'a clean::Item,
+ parent: &'a clean::Item,
+ tcx: TyCtxt<'tcx>,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+ display_fn(move |f| {
+ fn tag_html<'a>(
+ class: &'a str,
+ title: &'a str,
+ contents: &'a str,
+ ) -> impl fmt::Display + 'a {
+ display_fn(move |f| {
+ write!(
+ f,
+ r#"<span class="stab {}" title="{}">{}</span>"#,
+ class,
+ Escape(title),
+ contents
+ )
+ })
}
- tags += &tag_html("deprecated", "", message);
- }
- // The "rustc_private" crates are permanently unstable so it makes no sense
- // to render "unstable" everywhere.
- if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
- == Some(true)
- {
- tags += &tag_html("unstable", "", "Experimental");
- }
+ // The trailing space after each tag is to space it properly against the rest of the docs.
+ if let Some(depr) = &item.deprecation(tcx) {
+ let message = if stability::deprecation_in_effect(depr) {
+ "Deprecated"
+ } else {
+ "Deprecation planned"
+ };
+ write!(f, "{}", tag_html("deprecated", "", message))?;
+ }
- let cfg = match (&item.cfg, parent.cfg.as_ref()) {
- (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
- (cfg, _) => cfg.as_deref().cloned(),
- };
+ // The "rustc_private" crates are permanently unstable so it makes no sense
+ // to render "unstable" everywhere.
+ if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
+ == Some(true)
+ {
+ write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
+ }
- debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
- if let Some(ref cfg) = cfg {
- tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
- }
+ let cfg = match (&item.cfg, parent.cfg.as_ref()) {
+ (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
+ (cfg, _) => cfg.as_deref().cloned(),
+ };
- tags
+ debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
+ if let Some(ref cfg) = cfg {
+ write!(
+ f,
+ "{}",
+ tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html())
+ )
+ } else {
+ Ok(())
+ }
+ })
}
fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) {
@@ -522,12 +545,12 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
w.reserve(header_len);
write!(
w,
- "{vis}{constness}{asyncness}{unsafety}{abi}fn \
+ "{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
+ attrs = render_attributes_in_pre(it, ""),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -540,7 +563,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
notable_traits = notable_traits.unwrap_or_default(),
);
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
}
fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {
@@ -558,17 +581,17 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone();
// Output the trait definition
- wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
+ wrap_item(w, |mut w| {
write!(
w,
- "{}{}{}trait {}{}{}",
+ "{attrs}{}{}{}trait {}{}{}",
visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
t.unsafety(tcx).print_with_space(),
if t.is_auto(tcx) { "auto " } else { "" },
it.name.unwrap(),
t.generics.print(cx),
- bounds
+ bounds,
+ attrs = render_attributes_in_pre(it, ""),
);
if !t.generics.where_predicates.is_empty() {
@@ -588,7 +611,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
if should_hide_fields(count_types) {
toggle = true;
toggle_open(
- w,
+ &mut w,
format_args!("{} associated items", count_types + count_consts + count_methods),
);
}
@@ -612,7 +635,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
if !toggle && should_hide_fields(count_types + count_consts) {
toggle = true;
toggle_open(
- w,
+ &mut w,
format_args!(
"{} associated constant{} and {} method{}",
count_consts,
@@ -640,7 +663,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
if !toggle && should_hide_fields(count_methods) {
toggle = true;
- toggle_open(w, format_args!("{} methods", count_methods));
+ toggle_open(&mut w, format_args!("{} methods", count_methods));
}
if count_consts != 0 && count_methods != 0 {
w.write_str("\n");
@@ -688,14 +711,14 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
if toggle {
- toggle_close(w);
+ toggle_close(&mut w);
}
w.write_str("}");
}
});
// Trait documentation
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
write!(
@@ -713,7 +736,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let item_type = m.type_();
let id = cx.derive_id(format!("{}.{}", item_type, name));
let mut content = Buffer::empty_from(w);
- document(&mut content, cx, m, Some(t), HeadingOffset::H5);
+ write!(&mut content, "{}", document(cx, m, Some(t), HeadingOffset::H5));
let toggled = !content.is_empty();
if toggled {
let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
@@ -825,7 +848,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
// If there are methods directly on this trait object, render them here.
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All);
+ write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));
let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
@@ -858,8 +881,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
local.iter().partition(|i| i.inner_impl().kind.is_auto());
- synthetic.sort_by(|a, b| compare_impl(a, b, cx));
- concrete.sort_by(|a, b| compare_impl(a, b, cx));
+ synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
+ concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
@@ -1035,147 +1058,201 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
write!(
w,
- "trait {}{}{} = {};",
+ "{attrs}trait {}{}{} = {};",
it.name.unwrap(),
t.generics.print(cx),
print_where_clause(&t.generics, cx, 0, Ending::Newline),
- bounds(&t.bounds, true, cx)
+ bounds(&t.bounds, true, cx),
+ attrs = render_attributes_in_pre(it, ""),
);
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
}
fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
write!(
w,
- "type {}{}{where_clause} = impl {bounds};",
+ "{attrs}type {}{}{where_clause} = impl {bounds};",
it.name.unwrap(),
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
bounds = bounds(&t.bounds, false, cx),
+ attrs = render_attributes_in_pre(it, ""),
);
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
}
fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
write!(
w,
- "{}type {}{}{where_clause} = {type_};",
+ "{attrs}{}type {}{}{where_clause} = {type_};",
visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
it.name.unwrap(),
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
type_ = t.type_.print(cx),
+ attrs = render_attributes_in_pre(it, ""),
);
});
}
write_content(w, cx, it, t);
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
let def_id = it.item_id.expect_def_id();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
- document_type_layout(w, cx, def_id);
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
}
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
- wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
- render_union(w, it, Some(&s.generics), &s.fields, cx);
- });
+ #[derive(Template)]
+ #[template(path = "item_union.html")]
+ struct ItemUnion<'a, 'cx> {
+ cx: std::cell::RefCell<&'a mut Context<'cx>>,
+ it: &'a clean::Item,
+ s: &'a clean::Union,
+ }
- document(w, cx, it, None, HeadingOffset::H2);
+ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
+ fn render_assoc_items<'b>(
+ &'b self,
+ ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let def_id = self.it.item_id.expect_def_id();
+ let mut cx = self.cx.borrow_mut();
+ let v = render_assoc_items(*cx, self.it, def_id, AssocItemRender::All);
+ write!(f, "{v}")
+ })
+ }
+ fn document_type_layout<'b>(
+ &'b self,
+ ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let def_id = self.it.item_id.expect_def_id();
+ let cx = self.cx.borrow_mut();
+ let v = document_type_layout(*cx, def_id);
+ write!(f, "{v}")
+ })
+ }
+ fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let cx = self.cx.borrow_mut();
+ let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, *cx);
+ write!(f, "{v}")
+ })
+ }
+ fn render_attributes_in_pre<'b>(
+ &'b self,
+ ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let v = render_attributes_in_pre(self.it, "");
+ write!(f, "{v}")
+ })
+ }
+ fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let mut cx = self.cx.borrow_mut();
+ let v = document(*cx, self.it, None, HeadingOffset::H2);
+ write!(f, "{v}")
+ })
+ }
+ fn document_field<'b>(
+ &'b self,
+ field: &'a clean::Item,
+ ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let mut cx = self.cx.borrow_mut();
+ let v = document(*cx, field, Some(self.it), HeadingOffset::H3);
+ write!(f, "{v}")
+ })
+ }
+ fn stability_field(&self, field: &clean::Item) -> Option<String> {
+ let cx = self.cx.borrow();
+ field.stability_class(cx.tcx())
+ }
+ fn print_ty<'b>(
+ &'b self,
+ ty: &'a clean::Type,
+ ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let cx = self.cx.borrow();
+ let v = ty.print(*cx);
+ write!(f, "{v}")
+ })
+ }
- let mut fields = s
- .fields
- .iter()
- .filter_map(|f| match *f.kind {
- clean::StructFieldItem(ref ty) => Some((f, ty)),
- _ => None,
- })
- .peekable();
- if fields.peek().is_some() {
- write!(
- w,
- "<h2 id=\"fields\" class=\"fields small-section-header\">\
- Fields<a href=\"#fields\" class=\"anchor\">§</a>\
- </h2>"
- );
- for (field, ty) in fields {
- let name = field.name.expect("union field name");
- let id = format!("{}.{}", ItemType::StructField, name);
- write!(
- w,
- "<span id=\"{id}\" class=\"{shortty} small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\">§</a>\
- <code>{name}: {ty}</code>\
- </span>",
- shortty = ItemType::StructField,
- ty = ty.print(cx),
- );
- if let Some(stability_class) = field.stability_class(cx.tcx()) {
- write!(w, "<span class=\"stab {stability_class}\"></span>");
- }
- document(w, cx, field, Some(it), HeadingOffset::H3);
+ fn fields_iter(
+ &self,
+ ) -> std::iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>> {
+ self.s
+ .fields
+ .iter()
+ .filter_map(|f| match *f.kind {
+ clean::StructFieldItem(ref ty) => Some((f, ty)),
+ _ => None,
+ })
+ .peekable()
}
}
- let def_id = it.item_id.expect_def_id();
- render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
- document_type_layout(w, cx, def_id);
+
+ ItemUnion { cx: std::cell::RefCell::new(cx), it, s }.render_into(w).unwrap();
}
-fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) {
- for (i, ty) in s.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- match *ty.kind {
- clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"),
- clean::StructFieldItem(ref ty) => write!(w, "{}", ty.print(cx)),
- _ => unreachable!(),
+fn print_tuple_struct_fields<'a, 'cx: 'a>(
+ cx: &'a Context<'cx>,
+ s: &'a [clean::Item],
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(|f| {
+ for (i, ty) in s.iter().enumerate() {
+ if i > 0 {
+ f.write_str(", ")?;
+ }
+ match *ty.kind {
+ clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?,
+ clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?,
+ _ => unreachable!(),
+ }
}
- }
+ Ok(())
+ })
}
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
let tcx = cx.tcx();
let count_variants = e.variants().count();
- wrap_item(w, |w| {
- render_attributes_in_pre(w, it, "");
+ wrap_item(w, |mut w| {
write!(
w,
- "{}enum {}{}",
+ "{attrs}{}enum {}{}",
visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
it.name.unwrap(),
e.generics.print(cx),
+ attrs = render_attributes_in_pre(it, ""),
);
if !print_where_clause_and_check(w, &e.generics, cx) {
// If there wasn't a `where` clause, we add a whitespace.
@@ -1189,7 +1266,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w.write_str("{\n");
let toggle = should_hide_fields(count_variants);
if toggle {
- toggle_open(w, format_args!("{} variants", count_variants));
+ toggle_open(&mut w, format_args!("{} variants", count_variants));
}
for v in e.variants() {
w.write_str(" ");
@@ -1199,9 +1276,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
clean::VariantItem(ref var) => match var.kind {
clean::VariantKind::CLike => write!(w, "{}", name),
clean::VariantKind::Tuple(ref s) => {
- write!(w, "{}(", name);
- print_tuple_struct_fields(w, cx, s);
- w.write_str(")");
+ write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
}
clean::VariantKind::Struct(ref s) => {
render_struct(w, v, None, None, &s.fields, " ", false, cx);
@@ -1212,28 +1287,29 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w.write_str(",\n");
}
- if variants_stripped {
+ if variants_stripped && !it.is_non_exhaustive() {
w.write_str(" // some variants omitted\n");
}
if toggle {
- toggle_close(w);
+ toggle_close(&mut w);
}
w.write_str("}");
}
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if count_variants != 0 {
write!(
w,
"<h2 id=\"variants\" class=\"variants small-section-header\">\
Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
- </h2>",
- document_non_exhaustive_header(it)
+ </h2>\
+ {}\
+ <div class=\"variants\">",
+ document_non_exhaustive_header(it),
+ document_non_exhaustive(it)
);
- document_non_exhaustive(w, it);
- write!(w, "<div class=\"variants\">");
for variant in e.variants() {
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
@@ -1254,9 +1330,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
- w.write_str("(");
- print_tuple_struct_fields(w, cx, s);
- w.write_str(")");
+ write!(w, "({})", print_tuple_struct_fields(cx, s),);
}
w.write_str("</h3></section>");
@@ -1280,9 +1354,10 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"<div class=\"sub-variant\" id=\"{variant_id}\">\
- <h4>{heading}</h4>",
+ <h4>{heading}</h4>\
+ {}",
+ document_non_exhaustive(variant)
);
- document_non_exhaustive(w, variant);
for field in fields {
match *field.kind {
clean::StrippedItem(box clean::StructFieldItem(_)) => {}
@@ -1300,10 +1375,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
<code>{f}: {t}</code>\
</span>",
f = field.name.unwrap(),
- t = ty.print(cx)
+ t = ty.print(cx),
+ );
+ write!(
+ w,
+ "{}</div>",
+ document(cx, field, Some(variant), HeadingOffset::H5)
);
- document(w, cx, field, Some(variant), HeadingOffset::H5);
- write!(w, "</div>");
}
_ => unreachable!(),
}
@@ -1311,18 +1389,18 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w.write_str("</div>");
}
- document(w, cx, variant, Some(it), HeadingOffset::H4);
+ write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
}
write!(w, "</div>");
}
let def_id = it.item_id.expect_def_id();
- render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
- document_type_layout(w, cx, def_id);
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
}
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
highlight::render_item_decl_with_highlighting(&t.source, w);
- document(w, cx, it, None, HeadingOffset::H2)
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
}
fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
@@ -1348,14 +1426,14 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c
}
}
});
- document(w, cx, it, None, HeadingOffset::H2)
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
}
fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
let def_id = it.item_id.expect_def_id();
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
- render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+ 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.
@@ -1411,7 +1489,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
}
});
- document(w, cx, it, None, HeadingOffset::H2)
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
}
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
@@ -1420,7 +1498,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
let mut fields = s
.fields
@@ -1436,11 +1514,12 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
w,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
- </h2>",
+ </h2>\
+ {}",
if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
- document_non_exhaustive_header(it)
+ document_non_exhaustive_header(it),
+ document_non_exhaustive(it)
);
- document_non_exhaustive(w, it);
for (index, (field, ty)) in fields.enumerate() {
let field_name =
field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
@@ -1454,13 +1533,13 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
item_type = ItemType::StructField,
ty = ty.print(cx)
);
- document(w, cx, field, Some(it), HeadingOffset::H3);
+ write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3));
}
}
}
let def_id = it.item_id.expect_def_id();
- render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
- document_type_layout(w, cx, def_id);
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
}
fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1475,7 +1554,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
typ = s.type_.print(cx)
);
});
- document(w, cx, it, None, HeadingOffset::H2)
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
}
fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
@@ -1490,13 +1569,13 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
);
});
- document(w, cx, it, None, HeadingOffset::H2);
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
}
fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
- document(w, cx, it, None, HeadingOffset::H2)
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
}
/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
@@ -1575,12 +1654,25 @@ where
w.write_str("</code></pre>");
}
-fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
- let lhss = format!("{}", lhs.inner_impl().print(false, cx));
- let rhss = format!("{}", rhs.inner_impl().print(false, cx));
+#[derive(PartialEq, Eq)]
+struct ImplString(String);
- // lhs and rhs are formatted as HTML, which may be unnecessary
- compare_names(&lhss, &rhss)
+impl ImplString {
+ fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
+ ImplString(format!("{}", i.inner_impl().print(false, cx)))
+ }
+}
+
+impl PartialOrd for ImplString {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(Ord::cmp(self, other))
+ }
+}
+
+impl Ord for ImplString {
+ fn cmp(&self, other: &Self) -> Ordering {
+ compare_names(&self.0, &other.0)
+ }
}
fn render_implementor(
@@ -1620,64 +1712,69 @@ fn render_implementor(
);
}
-fn render_union(
- w: &mut Buffer,
- it: &clean::Item,
- g: Option<&clean::Generics>,
- fields: &[clean::Item],
- cx: &Context<'_>,
-) {
- let tcx = cx.tcx();
- write!(
- w,
- "{}union {}",
- visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
- it.name.unwrap(),
- );
-
- let where_displayed = g
- .map(|g| {
- write!(w, "{}", g.print(cx));
- print_where_clause_and_check(w, g, cx)
- })
- .unwrap_or(false);
+fn render_union<'a, 'cx: 'a>(
+ it: &'a clean::Item,
+ g: Option<&'a clean::Generics>,
+ fields: &'a [clean::Item],
+ cx: &'a Context<'cx>,
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(move |mut f| {
+ let tcx = cx.tcx();
+ write!(
+ f,
+ "{}union {}",
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+ it.name.unwrap(),
+ )?;
+
+ let where_displayed = g
+ .map(|g| {
+ let mut buf = Buffer::html();
+ write!(buf, "{}", g.print(cx));
+ let where_displayed = print_where_clause_and_check(&mut buf, g, cx);
+ write!(f, "{buf}", buf = buf.into_inner()).unwrap();
+ where_displayed
+ })
+ .unwrap_or(false);
- // If there wasn't a `where` clause, we add a whitespace.
- if !where_displayed {
- w.write_str(" ");
- }
+ // If there wasn't a `where` clause, we add a whitespace.
+ if !where_displayed {
+ f.write_str(" ")?;
+ }
- write!(w, "{{\n");
- let count_fields =
- fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count();
- let toggle = should_hide_fields(count_fields);
- if toggle {
- toggle_open(w, format_args!("{} fields", count_fields));
- }
+ write!(f, "{{\n")?;
+ let count_fields =
+ fields.iter().filter(|field| matches!(*field.kind, clean::StructFieldItem(..))).count();
+ let toggle = should_hide_fields(count_fields);
+ if toggle {
+ toggle_open(&mut f, format_args!("{} fields", count_fields));
+ }
- for field in fields {
- if let clean::StructFieldItem(ref ty) = *field.kind {
- write!(
- w,
- " {}{}: {},\n",
- visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
- field.name.unwrap(),
- ty.print(cx)
- );
+ for field in fields {
+ if let clean::StructFieldItem(ref ty) = *field.kind {
+ write!(
+ f,
+ " {}{}: {},\n",
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
+ field.name.unwrap(),
+ ty.print(cx)
+ )?;
+ }
}
- }
- if it.has_stripped_entries().unwrap() {
- write!(w, " /* private fields */\n");
- }
- if toggle {
- toggle_close(w);
- }
- w.write_str("}");
+ if it.has_stripped_entries().unwrap() {
+ write!(f, " /* private fields */\n")?;
+ }
+ if toggle {
+ toggle_close(&mut f);
+ }
+ f.write_str("}").unwrap();
+ Ok(())
+ })
}
fn render_struct(
- w: &mut Buffer,
+ mut w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
ty: Option<CtorKind>,
@@ -1699,10 +1796,11 @@ fn render_struct(
}
match ty {
None => {
- let where_diplayed = g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false);
+ let where_displayed =
+ g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false);
// If there wasn't a `where` clause, we add a whitespace.
- if !where_diplayed {
+ if !where_displayed {
w.write_str(" {");
} else {
w.write_str("{");
@@ -1712,7 +1810,7 @@ fn render_struct(
let has_visible_fields = count_fields > 0;
let toggle = should_hide_fields(count_fields);
if toggle {
- toggle_open(w, format_args!("{} fields", count_fields));
+ toggle_open(&mut w, format_args!("{} fields", count_fields));
}
for field in fields {
if let clean::StructFieldItem(ref ty) = *field.kind {
@@ -1736,7 +1834,7 @@ fn render_struct(
write!(w, " /* private fields */ ");
}
if toggle {
- toggle_close(w);
+ toggle_close(&mut w);
}
w.write_str("}");
}
@@ -1782,155 +1880,169 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
}
-fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
- if item.is_non_exhaustive() {
- write!(
- w,
- "<details class=\"toggle non-exhaustive\">\
- <summary class=\"hideme\"><span>{}</span></summary>\
- <div class=\"docblock\">",
- {
- if item.is_struct() {
- "This struct is marked as non-exhaustive"
- } else if item.is_enum() {
- "This enum is marked as non-exhaustive"
- } else if item.is_variant() {
- "This variant is marked as non-exhaustive"
- } else {
- "This type is marked as non-exhaustive"
+fn document_non_exhaustive<'a>(item: &'a clean::Item) -> impl fmt::Display + 'a {
+ display_fn(|f| {
+ if item.is_non_exhaustive() {
+ write!(
+ f,
+ "<details class=\"toggle non-exhaustive\">\
+ <summary class=\"hideme\"><span>{}</span></summary>\
+ <div class=\"docblock\">",
+ {
+ if item.is_struct() {
+ "This struct is marked as non-exhaustive"
+ } else if item.is_enum() {
+ "This enum is marked as non-exhaustive"
+ } else if item.is_variant() {
+ "This variant is marked as non-exhaustive"
+ } else {
+ "This type is marked as non-exhaustive"
+ }
}
+ )?;
+
+ if item.is_struct() {
+ f.write_str(
+ "Non-exhaustive structs could have additional fields added in future. \
+ Therefore, non-exhaustive structs cannot be constructed in external crates \
+ using the traditional <code>Struct { .. }</code> syntax; cannot be \
+ matched against without a wildcard <code>..</code>; and \
+ struct update syntax will not work.",
+ )?;
+ } else if item.is_enum() {
+ f.write_str(
+ "Non-exhaustive enums could have additional variants added in future. \
+ Therefore, when matching against variants of non-exhaustive enums, an \
+ extra wildcard arm must be added to account for any future variants.",
+ )?;
+ } else if item.is_variant() {
+ f.write_str(
+ "Non-exhaustive enum variants could have additional fields added in future. \
+ Therefore, non-exhaustive enum variants cannot be constructed in external \
+ crates and cannot be matched against.",
+ )?;
+ } else {
+ f.write_str(
+ "This type will require a wildcard arm in any match statements or constructors.",
+ )?;
}
- );
- if item.is_struct() {
- w.write_str(
- "Non-exhaustive structs could have additional fields added in future. \
- Therefore, non-exhaustive structs cannot be constructed in external crates \
- using the traditional <code>Struct { .. }</code> syntax; cannot be \
- matched against without a wildcard <code>..</code>; and \
- struct update syntax will not work.",
- );
- } else if item.is_enum() {
- w.write_str(
- "Non-exhaustive enums could have additional variants added in future. \
- Therefore, when matching against variants of non-exhaustive enums, an \
- extra wildcard arm must be added to account for any future variants.",
- );
- } else if item.is_variant() {
- w.write_str(
- "Non-exhaustive enum variants could have additional fields added in future. \
- Therefore, non-exhaustive enum variants cannot be constructed in external \
- crates and cannot be matched against.",
- );
- } else {
- w.write_str(
- "This type will require a wildcard arm in any match statements or constructors.",
- );
+ f.write_str("</div></details>")?;
}
-
- w.write_str("</div></details>");
- }
+ Ok(())
+ })
}
-fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
- fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) {
+fn document_type_layout<'a, 'cx: 'a>(
+ cx: &'a Context<'cx>,
+ ty_def_id: DefId,
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ fn write_size_of_layout(mut w: impl fmt::Write, layout: &LayoutS, tag_size: u64) {
if layout.abi.is_unsized() {
- write!(w, "(unsized)");
+ write!(w, "(unsized)").unwrap();
} else {
let size = layout.size.bytes() - tag_size;
- write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },);
+ write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" }).unwrap();
+ if layout.abi.is_uninhabited() {
+ write!(
+ w,
+ " (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
+ ).unwrap();
+ }
}
}
- if !cx.shared.show_type_layout {
- return;
- }
-
- writeln!(
- w,
- "<h2 id=\"layout\" class=\"small-section-header\"> \
- Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
- );
- writeln!(w, "<div class=\"docblock\">");
-
- let tcx = cx.tcx();
- let param_env = tcx.param_env(ty_def_id);
- let ty = tcx.type_of(ty_def_id).subst_identity();
- match tcx.layout_of(param_env.and(ty)) {
- Ok(ty_layout) => {
- writeln!(
- w,
- "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
- <strong>completely unstable</strong> and may even differ between compilations. \
- The only exception is types with certain <code>repr(...)</code> attributes. \
- Please see the Rust Reference’s \
- <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
- chapter for details on type layout guarantees.</p></div>"
- );
- w.write_str("<p><strong>Size:</strong> ");
- write_size_of_layout(w, &ty_layout.layout.0, 0);
- writeln!(w, "</p>");
- if let Variants::Multiple { variants, tag, tag_encoding, .. } =
- &ty_layout.layout.variants()
- {
- if !variants.is_empty() {
- w.write_str(
- "<p><strong>Size for each variant:</strong></p>\
- <ul>",
- );
-
- let Adt(adt, _) = ty_layout.ty.kind() else {
- span_bug!(tcx.def_span(ty_def_id), "not an adt")
- };
+ display_fn(move |mut f| {
+ if !cx.shared.show_type_layout {
+ return Ok(());
+ }
- let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
- 0
- } else if let Primitive::Int(i, _) = tag.primitive() {
- i.size().bytes()
- } else {
- span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
- };
+ writeln!(
+ f,
+ "<h2 id=\"layout\" class=\"small-section-header\"> \
+ Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
+ )?;
+ writeln!(f, "<div class=\"docblock\">")?;
- for (index, layout) in variants.iter_enumerated() {
- let name = adt.variant(index).name;
- write!(w, "<li><code>{name}</code>: ");
- write_size_of_layout(w, layout, tag_size);
- writeln!(w, "</li>");
+ let tcx = cx.tcx();
+ let param_env = tcx.param_env(ty_def_id);
+ let ty = tcx.type_of(ty_def_id).subst_identity();
+ match tcx.layout_of(param_env.and(ty)) {
+ Ok(ty_layout) => {
+ writeln!(
+ f,
+ "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
+ <strong>completely unstable</strong> and may even differ between compilations. \
+ The only exception is types with certain <code>repr(...)</code> attributes. \
+ Please see the Rust Reference’s \
+ <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
+ chapter for details on type layout guarantees.</p></div>"
+ )?;
+ f.write_str("<p><strong>Size:</strong> ")?;
+ write_size_of_layout(&mut f, &ty_layout.layout.0, 0);
+ writeln!(f, "</p>")?;
+ if let Variants::Multiple { variants, tag, tag_encoding, .. } =
+ &ty_layout.layout.variants()
+ {
+ if !variants.is_empty() {
+ f.write_str(
+ "<p><strong>Size for each variant:</strong></p>\
+ <ul>",
+ )?;
+
+ let Adt(adt, _) = ty_layout.ty.kind() else {
+ span_bug!(tcx.def_span(ty_def_id), "not an adt")
+ };
+
+ let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
+ 0
+ } else if let Primitive::Int(i, _) = tag.primitive() {
+ i.size().bytes()
+ } else {
+ span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
+ };
+
+ for (index, layout) in variants.iter_enumerated() {
+ let name = adt.variant(index).name;
+ write!(&mut f, "<li><code>{name}</code>: ")?;
+ write_size_of_layout(&mut f, layout, tag_size);
+ writeln!(&mut f, "</li>")?;
+ }
+ f.write_str("</ul>")?;
}
- w.write_str("</ul>");
}
}
+ // This kind of layout error can occur with valid code, e.g. if you try to
+ // get the layout of a generic type such as `Vec<T>`.
+ Err(LayoutError::Unknown(_)) => {
+ writeln!(
+ f,
+ "<p><strong>Note:</strong> Unable to compute type layout, \
+ possibly due to this type having generic parameters. \
+ Layout can only be computed for concrete, fully-instantiated types.</p>"
+ )?;
+ }
+ // This kind of error probably can't happen with valid code, but we don't
+ // want to panic and prevent the docs from building, so we just let the
+ // user know that we couldn't compute the layout.
+ Err(LayoutError::SizeOverflow(_)) => {
+ writeln!(
+ f,
+ "<p><strong>Note:</strong> Encountered an error during type layout; \
+ the type was too big.</p>"
+ )?;
+ }
+ Err(LayoutError::NormalizationFailure(_, _)) => {
+ writeln!(
+ f,
+ "<p><strong>Note:</strong> Encountered an error during type layout; \
+ the type failed to be normalized.</p>"
+ )?;
+ }
}
- // This kind of layout error can occur with valid code, e.g. if you try to
- // get the layout of a generic type such as `Vec<T>`.
- Err(LayoutError::Unknown(_)) => {
- writeln!(
- w,
- "<p><strong>Note:</strong> Unable to compute type layout, \
- possibly due to this type having generic parameters. \
- Layout can only be computed for concrete, fully-instantiated types.</p>"
- );
- }
- // This kind of error probably can't happen with valid code, but we don't
- // want to panic and prevent the docs from building, so we just let the
- // user know that we couldn't compute the layout.
- Err(LayoutError::SizeOverflow(_)) => {
- writeln!(
- w,
- "<p><strong>Note:</strong> Encountered an error during type layout; \
- the type was too big.</p>"
- );
- }
- Err(LayoutError::NormalizationFailure(_, _)) => {
- writeln!(
- w,
- "<p><strong>Note:</strong> Encountered an error during type layout; \
- the type failed to be normalized.</p>"
- )
- }
- }
- writeln!(w, "</div>");
+ writeln!(f, "</div>")
+ })
}
fn pluralize(count: usize) -> &'static str {
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 090ea2cb1..f5b4a3f5a 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -7,9 +7,7 @@ use rustc_span::symbol::Symbol;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use crate::clean;
-use crate::clean::types::{
- FnRetTy, Function, GenericBound, Generics, ItemId, Type, WherePredicate,
-};
+use crate::clean::types::{FnRetTy, Function, Generics, ItemId, Type, WherePredicate};
use crate::formats::cache::{Cache, OrphanImplItem};
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
@@ -42,6 +40,7 @@ pub(crate) fn build_index<'tcx>(
parent_idx: None,
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
aliases: item.attrs.get_doc_aliases(),
+ deprecation: item.deprecation(tcx),
});
}
}
@@ -60,7 +59,7 @@ pub(crate) fn build_index<'tcx>(
// `sort_unstable_by_key` produces lifetime errors
let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent);
let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent);
- std::cmp::Ord::cmp(&k1, &k2)
+ Ord::cmp(&k1, &k2)
});
// Set up alias indexes.
@@ -253,7 +252,17 @@ pub(crate) fn build_index<'tcx>(
)?;
crate_data.serialize_field(
"q",
- &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
+ &self
+ .items
+ .iter()
+ .enumerate()
+ // Serialize as an array of item indices and full paths
+ .filter_map(
+ |(index, item)| {
+ if item.path.is_empty() { None } else { Some((index, &item.path)) }
+ },
+ )
+ .collect::<Vec<_>>(),
)?;
crate_data.serialize_field(
"d",
@@ -307,6 +316,16 @@ pub(crate) fn build_index<'tcx>(
.collect::<Vec<_>>(),
)?;
crate_data.serialize_field(
+ "c",
+ &self
+ .items
+ .iter()
+ .enumerate()
+ // Serialize as an array of deprecated item indices
+ .filter_map(|(index, item)| item.deprecation.map(|_| index))
+ .collect::<Vec<_>>(),
+ )?;
+ crate_data.serialize_field(
"p",
&self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
)?;
@@ -467,7 +486,7 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
}
// First, check if it's "Self".
- let arg = if let Some(self_) = self_ {
+ let mut arg = if let Some(self_) = self_ {
match &*arg {
Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_,
type_ if type_.is_self_type() => self_,
@@ -477,34 +496,33 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
arg
};
+ // strip references from the argument type
+ while let Type::BorrowedRef { type_, .. } = &*arg {
+ arg = &*type_;
+ }
+
// If this argument is a type parameter and not a trait bound or a type, we need to look
// for its bounds.
if let Type::Generic(arg_s) = *arg {
// First we check if the bounds are in a `where` predicate...
- if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
- WherePredicate::BoundPredicate { ty, .. } => ty.def_id(cache) == arg.def_id(cache),
+ for where_pred in generics.where_predicates.iter().filter(|g| match g {
+ WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
_ => false,
}) {
let mut ty_generics = Vec::new();
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
for bound in bounds.iter() {
- if let GenericBound::TraitBound(poly_trait, _) = bound {
- for param_def in poly_trait.generic_params.iter() {
- match &param_def.kind {
- clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
- add_generics_and_bounds_as_types(
- self_,
- generics,
- ty,
- tcx,
- recurse + 1,
- &mut ty_generics,
- cache,
- )
- }
- _ => {}
- }
- }
+ if let Some(path) = bound.get_trait_path() {
+ let ty = Type::Path { path };
+ add_generics_and_bounds_as_types(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ cache,
+ );
}
}
insert_ty(res, arg.clone(), ty_generics);
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
new file mode 100644
index 000000000..455b4e9ae
--- /dev/null
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -0,0 +1,558 @@
+use std::{borrow::Cow, rc::Rc};
+
+use askama::Template;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::{
+ clean,
+ formats::{item_type::ItemType, Impl},
+ html::{format::Buffer, markdown::IdMap},
+};
+
+use super::{item_ty_to_section, Context, ItemSection};
+
+#[derive(Template)]
+#[template(path = "sidebar.html")]
+pub(super) struct Sidebar<'a> {
+ pub(super) title_prefix: &'static str,
+ pub(super) title: &'a str,
+ pub(super) is_crate: bool,
+ pub(super) version: &'a str,
+ pub(super) blocks: Vec<LinkBlock<'a>>,
+ pub(super) path: String,
+}
+
+impl<'a> Sidebar<'a> {
+ /// Only create a `<section>` if there are any blocks
+ /// which should actually be rendered.
+ pub fn should_render_blocks(&self) -> bool {
+ self.blocks.iter().any(LinkBlock::should_render)
+ }
+}
+
+/// A sidebar section such as 'Methods'.
+pub(crate) struct LinkBlock<'a> {
+ /// The name of this section, e.g. 'Methods'
+ /// as well as the link to it, e.g. `#implementations`.
+ /// Will be rendered inside an `<h3>` tag
+ heading: Link<'a>,
+ links: Vec<Link<'a>>,
+ /// Render the heading even if there are no links
+ force_render: bool,
+}
+
+impl<'a> LinkBlock<'a> {
+ pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
+ Self { heading, links, force_render: false }
+ }
+
+ pub fn forced(heading: Link<'a>) -> Self {
+ Self { heading, links: vec![], force_render: true }
+ }
+
+ pub fn should_render(&self) -> bool {
+ self.force_render || !self.links.is_empty()
+ }
+}
+
+/// A link to an item. Content should not be escaped.
+#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Link<'a> {
+ /// The content for the anchor tag
+ name: Cow<'a, str>,
+ /// The id of an anchor within the page (without a `#` prefix)
+ href: Cow<'a, str>,
+}
+
+impl<'a> Link<'a> {
+ pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
+ Self { href: href.into(), name: name.into() }
+ }
+ pub fn empty() -> Link<'static> {
+ Link::new("", "")
+ }
+}
+
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+ let blocks: Vec<LinkBlock<'_>> = match *it.kind {
+ clean::StructItem(ref s) => sidebar_struct(cx, it, s),
+ clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
+ clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
+ clean::UnionItem(ref u) => sidebar_union(cx, it, u),
+ clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
+ clean::TypedefItem(_) => sidebar_typedef(cx, it),
+ clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
+ clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
+ _ => vec![],
+ };
+ // The sidebar is designed to display sibling functions, modules and
+ // other miscellaneous information. since there are lots of sibling
+ // items (and that causes quadratic growth in large modules),
+ // we refactor common parts into a shared JavaScript file per module.
+ // still, we don't move everything into JS because we want to preserve
+ // as much HTML as possible in order to allow non-JS-enabled browsers
+ // to navigate the documentation (though slightly inefficiently).
+ let (title_prefix, title) = if it.is_struct()
+ || it.is_trait()
+ || it.is_primitive()
+ || it.is_union()
+ || it.is_enum()
+ || it.is_mod()
+ || it.is_typedef()
+ {
+ (
+ match *it.kind {
+ clean::ModuleItem(..) if it.is_crate() => "Crate ",
+ clean::ModuleItem(..) => "Module ",
+ _ => "",
+ },
+ it.name.as_ref().unwrap().as_str(),
+ )
+ } else {
+ ("", "")
+ };
+ let version =
+ if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
+ let path: String = if !it.is_mod() {
+ cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+ } else {
+ "".into()
+ };
+ let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+ sidebar.render_into(buffer).unwrap();
+}
+
+fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
+ let mut fields = fields
+ .iter()
+ .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
+ .filter_map(|f| {
+ f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
+ })
+ .collect::<Vec<Link<'a>>>();
+ fields.sort();
+ fields
+}
+
+fn sidebar_struct<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ s: &'a clean::Struct,
+) -> Vec<LinkBlock<'a>> {
+ let fields = get_struct_fields_name(&s.fields);
+ let field_name = match s.ctor_kind {
+ Some(CtorKind::Fn) => Some("Tuple Fields"),
+ None => Some("Fields"),
+ _ => None,
+ };
+ let mut items = vec![];
+ if let Some(name) = field_name {
+ items.push(LinkBlock::new(Link::new("fields", name), fields));
+ }
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+fn sidebar_trait<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ t: &'a clean::Trait,
+) -> Vec<LinkBlock<'a>> {
+ fn filter_items<'a>(
+ items: &'a [clean::Item],
+ filt: impl Fn(&clean::Item) -> bool,
+ ty: &str,
+ ) -> Vec<Link<'a>> {
+ let mut res = items
+ .iter()
+ .filter_map(|m: &clean::Item| match m.name {
+ Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
+ _ => None,
+ })
+ .collect::<Vec<Link<'a>>>();
+ res.sort();
+ res
+ }
+
+ let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+ let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
+ let req_assoc_const =
+ filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+ let prov_assoc_const =
+ filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
+ let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
+ let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
+ let mut foreign_impls = vec![];
+ if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
+ foreign_impls.extend(
+ implementors
+ .iter()
+ .filter(|i| !i.is_on_local_type(cx))
+ .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
+ .map(|(name, id)| Link::new(id, name)),
+ );
+ foreign_impls.sort();
+ }
+
+ let mut blocks: Vec<LinkBlock<'_>> = [
+ ("required-associated-types", "Required Associated Types", req_assoc),
+ ("provided-associated-types", "Provided Associated Types", prov_assoc),
+ ("required-associated-consts", "Required Associated Constants", req_assoc_const),
+ ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
+ ("required-methods", "Required Methods", req_method),
+ ("provided-methods", "Provided Methods", prov_method),
+ ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
+ ]
+ .into_iter()
+ .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+ .collect();
+ sidebar_assoc_items(cx, it, &mut blocks);
+ blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+ if t.is_auto(cx.tcx()) {
+ blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+ }
+ blocks
+}
+
+fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+ } else {
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) =
+ super::get_filtered_impls_for_reference(&shared, it);
+
+ sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
+ }
+}
+
+fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+fn sidebar_union<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ u: &'a clean::Union,
+) -> Vec<LinkBlock<'a>> {
+ let fields = get_struct_fields_name(&u.fields);
+ let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+/// Adds trait implementations into the blocks of links
+fn sidebar_assoc_items<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ links: &mut Vec<LinkBlock<'a>>,
+) {
+ let did = it.item_id.expect_def_id();
+ let cache = cx.cache();
+
+ let mut assoc_consts = Vec::new();
+ let mut methods = Vec::new();
+ if let Some(v) = cache.impls.get(&did) {
+ let mut used_links = FxHashSet::default();
+ let mut id_map = IdMap::new();
+
+ {
+ let used_links_bor = &mut used_links;
+ assoc_consts.extend(
+ v.iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
+ );
+ // We want links' order to be reproducible so we don't use unstable sort.
+ assoc_consts.sort();
+
+ #[rustfmt::skip] // rustfmt makes the pipeline less readable
+ methods.extend(
+ v.iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
+ );
+
+ // We want links' order to be reproducible so we don't use unstable sort.
+ methods.sort();
+ }
+
+ let mut deref_methods = Vec::new();
+ let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
+ if let Some(impl_) =
+ v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+ {
+ let mut derefs = DefIdSet::default();
+ derefs.insert(did);
+ sidebar_deref_methods(
+ cx,
+ &mut deref_methods,
+ impl_,
+ v,
+ &mut derefs,
+ &mut used_links,
+ );
+ }
+
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
+ let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
+
+ sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
+ } else {
+ std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+ };
+
+ let mut blocks = vec![
+ LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
+ LinkBlock::new(Link::new("implementations", "Methods"), methods),
+ ];
+ blocks.append(&mut deref_methods);
+ blocks.extend([concrete, synthetic, blanket]);
+ links.append(&mut blocks);
+ }
+}
+
+fn sidebar_deref_methods<'a>(
+ cx: &'a Context<'_>,
+ out: &mut Vec<LinkBlock<'a>>,
+ impl_: &Impl,
+ v: &[Impl],
+ derefs: &mut DefIdSet,
+ used_links: &mut FxHashSet<String>,
+) {
+ let c = cx.cache();
+
+ debug!("found Deref: {:?}", impl_);
+ if let Some((target, real_target)) =
+ impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
+ clean::AssocTypeItem(box ref t, _) => Some(match *t {
+ clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ _ => (&t.type_, &t.type_),
+ }),
+ _ => None,
+ })
+ {
+ debug!("found target, real_target: {:?} {:?}", target, real_target);
+ if let Some(did) = target.def_id(c) &&
+ let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
+ // `impl Deref<Target = S> for S`
+ (did == type_did || !derefs.insert(did))
+ {
+ // Avoid infinite cycles
+ return;
+ }
+ let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let inner_impl = target
+ .def_id(c)
+ .or_else(|| {
+ target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
+ })
+ .and_then(|did| c.impls.get(&did));
+ if let Some(impls) = inner_impl {
+ debug!("found inner_impl: {:?}", impls);
+ let mut ret = impls
+ .iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
+ .collect::<Vec<_>>();
+ if !ret.is_empty() {
+ let id = if let Some(target_def_id) = real_target.def_id(c) {
+ Cow::Borrowed(
+ cx.deref_id_map
+ .get(&target_def_id)
+ .expect("Deref section without derived id")
+ .as_str(),
+ )
+ } else {
+ Cow::Borrowed("deref-methods")
+ };
+ let title = format!(
+ "Methods from {:#}<Target={:#}>",
+ impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
+ real_target.print(cx),
+ );
+ // We want links' order to be reproducible so we don't use unstable sort.
+ ret.sort();
+ out.push(LinkBlock::new(Link::new(id, title), ret));
+ }
+ }
+
+ // Recurse into any further impls that might exist for `target`
+ if let Some(target_did) = target.def_id(c) &&
+ let Some(target_impls) = c.impls.get(&target_did) &&
+ let Some(target_deref_impl) = target_impls.iter().find(|i| {
+ i.inner_impl()
+ .trait_
+ .as_ref()
+ .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
+ .unwrap_or(false)
+ })
+ {
+ sidebar_deref_methods(
+ cx,
+ out,
+ target_deref_impl,
+ target_impls,
+ derefs,
+ used_links,
+ );
+ }
+ }
+}
+
+fn sidebar_enum<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ e: &'a clean::Enum,
+) -> Vec<LinkBlock<'a>> {
+ let mut variants = e
+ .variants()
+ .filter_map(|v| v.name)
+ .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+ .collect::<Vec<_>>();
+ variants.sort_unstable();
+
+ let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+pub(crate) fn sidebar_module_like(
+ item_sections_in_use: FxHashSet<ItemSection>,
+) -> LinkBlock<'static> {
+ let item_sections = ItemSection::ALL
+ .iter()
+ .copied()
+ .filter(|sec| item_sections_in_use.contains(sec))
+ .map(|sec| Link::new(sec.id(), sec.name()))
+ .collect();
+ LinkBlock::new(Link::empty(), item_sections)
+}
+
+fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
+ let item_sections_in_use: FxHashSet<_> = items
+ .iter()
+ .filter(|it| {
+ !it.is_stripped()
+ && it
+ .name
+ .or_else(|| {
+ if let clean::ImportItem(ref i) = *it.kind &&
+ let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+ })
+ .is_some()
+ })
+ .map(|it| item_ty_to_section(it.type_()))
+ .collect();
+
+ sidebar_module_like(item_sections_in_use)
+}
+
+fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+/// Renders the trait implementations for this type
+fn sidebar_render_assoc_items(
+ cx: &Context<'_>,
+ id_map: &mut IdMap,
+ concrete: Vec<&Impl>,
+ synthetic: Vec<&Impl>,
+ blanket_impl: Vec<&Impl>,
+) -> [LinkBlock<'static>; 3] {
+ let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+ let mut links = FxHashSet::default();
+
+ let mut ret = impls
+ .iter()
+ .filter_map(|it| {
+ let trait_ = it.inner_impl().trait_.as_ref()?;
+ let encoded =
+ id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+ let prefix = match it.inner_impl().polarity {
+ ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+ ty::ImplPolarity::Negative => "!",
+ };
+ let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
+ if links.insert(generated.clone()) { Some(generated) } else { None }
+ })
+ .collect::<Vec<Link<'static>>>();
+ ret.sort();
+ ret
+ };
+
+ let concrete = format_impls(concrete, id_map);
+ let synthetic = format_impls(synthetic, id_map);
+ let blanket = format_impls(blanket_impl, id_map);
+ [
+ LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+ LinkBlock::new(
+ Link::new("synthetic-implementations", "Auto Trait Implementations"),
+ synthetic,
+ ),
+ LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+ ]
+}
+
+fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
+ if used_links.insert(url.clone()) {
+ return url;
+ }
+ let mut add = 1;
+ while !used_links.insert(format!("{}-{}", url, add)) {
+ add += 1;
+ }
+ format!("{}-{}", url, add)
+}
+
+fn get_methods<'a>(
+ i: &'a clean::Impl,
+ for_deref: bool,
+ used_links: &mut FxHashSet<String>,
+ deref_mut: bool,
+ tcx: TyCtxt<'_>,
+) -> Vec<Link<'a>> {
+ i.items
+ .iter()
+ .filter_map(|item| match item.name {
+ Some(ref name) if !name.is_empty() && item.is_method() => {
+ if !for_deref || super::should_render_item(item, deref_mut, tcx) {
+ Some(Link::new(
+ get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
+ name.as_str(),
+ ))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ .collect::<Vec<_>>()
+}
+
+fn get_associated_constants<'a>(
+ i: &'a clean::Impl,
+ used_links: &mut FxHashSet<String>,
+) -> Vec<Link<'a>> {
+ i.items
+ .iter()
+ .filter_map(|item| match item.name {
+ Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
+ get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
+ name.as_str(),
+ )),
+ _ => None,
+ })
+ .collect::<Vec<_>>()
+}
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 4514894ca..eb9262f47 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -29,12 +29,12 @@ pub(crate) enum LinkFromSrc {
/// This function will do at most two things:
///
-/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`.
+/// 1. Generate a `span` correspondence map which links an item `span` to its definition `span`.
/// 2. Collect the source code files.
///
-/// It returns the `krate`, the source code files and the `span` correspondance map.
+/// It returns the `krate`, the source code files and the `span` correspondence map.
///
-/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't
+/// Note about the `span` correspondence map: the keys are actually `(lo, hi)` of `span`s. We don't
/// need the `span` context later on, only their position, so instead of keep a whole `Span`, we
/// only keep the `lo` and `hi`.
pub(crate) fn collect_spans_and_sources(