summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r--src/librustdoc/html/render/context.rs21
-rw-r--r--src/librustdoc/html/render/mod.rs89
-rw-r--r--src/librustdoc/html/render/print_item.rs107
-rw-r--r--src/librustdoc/html/render/search_index.rs433
-rw-r--r--src/librustdoc/html/render/sidebar.rs24
-rw-r--r--src/librustdoc/html/render/span_map.rs4
-rw-r--r--src/librustdoc/html/render/type_layout.rs59
-rw-r--r--src/librustdoc/html/render/write_shared.rs33
8 files changed, 568 insertions, 202 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 50777134d..f0199703c 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -176,9 +176,9 @@ impl<'tcx> Context<'tcx> {
let mut render_redirect_pages = self.render_redirect_pages;
// If the item is stripped but inlined, links won't point to the item so no need to generate
// a file for it.
- if it.is_stripped() &&
- let Some(def_id) = it.def_id() &&
- def_id.is_local()
+ if it.is_stripped()
+ && let Some(def_id) = it.def_id()
+ && def_id.is_local()
{
if self.is_inside_inlined_module || self.shared.cache.inlined_items.contains(&def_id) {
// For now we're forced to generate a redirect page for stripped items until
@@ -371,7 +371,9 @@ impl<'tcx> Context<'tcx> {
path = href.into_inner().to_string_lossy().into_owned();
- if let Some(c) = path.as_bytes().last() && *c != b'/' {
+ if let Some(c) = path.as_bytes().last()
+ && *c != b'/'
+ {
path.push('/');
}
@@ -741,9 +743,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(scrape_examples_help_file, v)?;
}
- if let Some(ref redirections) = shared.redirections && !redirections.borrow().is_empty() {
- let redirect_map_path =
- self.dst.join(crate_name.as_str()).join("redirect-map.json");
+ if let Some(ref redirections) = shared.redirections
+ && !redirections.borrow().is_empty()
+ {
+ let redirect_map_path = self.dst.join(crate_name.as_str()).join("redirect-map.json");
let paths = serde_json::to_string(&*redirections.borrow()).unwrap();
shared.ensure_dir(&self.dst.join(crate_name.as_str()))?;
shared.fs.write(redirect_map_path, paths)?;
@@ -790,7 +793,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
}
if !self.is_inside_inlined_module {
- if let Some(def_id) = item.def_id() && self.cache().inlined_items.contains(&def_id) {
+ if let Some(def_id) = item.def_id()
+ && self.cache().inlined_items.contains(&def_id)
+ {
self.is_inside_inlined_module = true;
}
} else if !self.cache().document_hidden && item.is_doc_hidden() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index c52fa01bd..34350c2ed 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -45,7 +45,6 @@ use std::iter::Peekable;
use std::path::PathBuf;
use std::rc::Rc;
use std::str;
-use std::string::ToString;
use askama::Template;
use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
@@ -66,7 +65,7 @@ use crate::clean::{self, ItemId, RenderedLink, SelfTy};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, Impl, RenderMode};
+use crate::formats::Impl;
use crate::html::escape::Escape;
use crate::html::format::{
display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
@@ -89,6 +88,21 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
})
}
+/// Specifies whether rendering directly implemented trait items or ones from a certain Deref
+/// impl.
+pub(crate) enum AssocItemRender<'a> {
+ All,
+ DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
+}
+
+/// For different handling of associated items from the Deref target of a type rather than the type
+/// itself.
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum RenderMode {
+ Normal,
+ ForDeref { mut_: bool },
+}
+
// Helper structs for rendering items/sidebars and carrying along contextual
// information
@@ -113,6 +127,7 @@ pub(crate) struct IndexItem {
pub(crate) struct RenderType {
id: Option<RenderTypeId>,
generics: Option<Vec<RenderType>>,
+ bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
}
impl Serialize for RenderType {
@@ -129,10 +144,15 @@ impl Serialize for RenderType {
Some(RenderTypeId::Index(idx)) => *idx,
_ => panic!("must convert render types to indexes before serializing"),
};
- if let Some(generics) = &self.generics {
+ if self.generics.is_some() || self.bindings.is_some() {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&id)?;
- seq.serialize_element(generics)?;
+ seq.serialize_element(self.generics.as_ref().map(Vec::as_slice).unwrap_or_default())?;
+ if self.bindings.is_some() {
+ seq.serialize_element(
+ self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default(),
+ )?;
+ }
seq.end()
} else {
id.serialize(serializer)
@@ -140,13 +160,31 @@ impl Serialize for RenderType {
}
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum RenderTypeId {
DefId(DefId),
Primitive(clean::PrimitiveType),
+ AssociatedType(Symbol),
Index(isize),
}
+impl Serialize for RenderTypeId {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let id = match &self {
+ // 0 is a sentinel, everything else is one-indexed
+ // concrete type
+ RenderTypeId::Index(idx) if *idx >= 0 => idx + 1,
+ // generic type parameter
+ RenderTypeId::Index(idx) => *idx,
+ _ => panic!("must convert render types to indexes before serializing"),
+ };
+ id.serialize(serializer)
+ }
+}
+
/// Full type of functions/methods in the search index.
#[derive(Debug)]
pub(crate) struct IndexItemFunctionType {
@@ -171,16 +209,23 @@ impl Serialize for IndexItemFunctionType {
} else {
let mut seq = serializer.serialize_seq(None)?;
match &self.inputs[..] {
- [one] if one.generics.is_none() => seq.serialize_element(one)?,
+ [one] if one.generics.is_none() && one.bindings.is_none() => {
+ seq.serialize_element(one)?
+ }
_ => seq.serialize_element(&self.inputs)?,
}
match &self.output[..] {
[] if self.where_clause.is_empty() => {}
- [one] if one.generics.is_none() => seq.serialize_element(one)?,
+ [one] if one.generics.is_none() && one.bindings.is_none() => {
+ seq.serialize_element(one)?
+ }
_ => seq.serialize_element(&self.output)?,
}
for constraint in &self.where_clause {
- if let [one] = &constraint[..] && one.generics.is_none() {
+ if let [one] = &constraint[..]
+ && one.generics.is_none()
+ && one.bindings.is_none()
+ {
seq.serialize_element(one)?;
} else {
seq.serialize_element(constraint)?;
@@ -627,7 +672,7 @@ fn short_item_info(
format!("Deprecating in {version}")
}
}
- DeprecatedSince::Future => String::from("Deprecating in a future Rust version"),
+ DeprecatedSince::Future => String::from("Deprecating in a future version"),
DeprecatedSince::NonStandard(since) => {
format!("Deprecated since {}", Escape(since.as_str()))
}
@@ -915,7 +960,9 @@ fn render_stability_since_raw_with_extra(
containing_const_ver: Option<StableSince>,
extra_class: &str,
) -> bool {
- let stable_version = if ver != containing_ver && let Some(ver) = &ver {
+ let stable_version = if ver != containing_ver
+ && let Some(ver) = &ver
+ {
since_to_string(ver)
} else {
None
@@ -1097,7 +1144,7 @@ impl<'a> AssocItemLink<'a> {
fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) {
write!(
w,
- "<h2 id=\"{id}\" class=\"small-section-header\">\
+ "<h2 id=\"{id}\" class=\"section-header\">\
{title}\
<a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
@@ -1348,8 +1395,7 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O
if let Some(trait_) = &impl_.trait_ {
let trait_did = trait_.def_id();
- if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
- {
+ if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
has_notable_trait = true;
}
}
@@ -1384,7 +1430,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
if let Some(trait_) = &impl_.trait_ {
let trait_did = trait_.def_id();
- if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) {
+ if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) {
if out.is_empty() {
write!(
&mut out,
@@ -1394,15 +1440,10 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
);
}
- //use the "where" class here to make it small
- write!(
- &mut out,
- "<span class=\"where fmt-newline\">{}</span>",
- impl_.print(false, cx)
- );
+ write!(&mut out, "<div class=\"where\">{}</div>", impl_.print(false, cx));
for it in &impl_.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
- out.push_str("<span class=\"where fmt-newline\"> ");
+ out.push_str("<div class=\"where\"> ");
let empty_set = FxHashSet::default();
let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
assoc_type(
@@ -1415,7 +1456,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
0,
cx,
);
- out.push_str(";</span>");
+ out.push_str(";</div>");
}
}
}
@@ -1901,7 +1942,7 @@ pub(crate) fn render_impl_summary(
if show_def_docs {
for it in &inner_impl.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
- w.write_str("<span class=\"where fmt-newline\"> ");
+ w.write_str("<div class=\"where\"> ");
assoc_type(
w,
it,
@@ -1912,7 +1953,7 @@ pub(crate) fn render_impl_summary(
0,
cx,
);
- w.write_str(";</span>");
+ w.write_str(";</div>");
}
}
}
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index d226701ba..5ca623f01 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1,5 +1,3 @@
-use clean::AttributesExt;
-
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
@@ -22,12 +20,13 @@ use super::{
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
render_impl, render_rightside, render_stability_since_raw,
- render_stability_since_raw_with_extra, AssocItemLink, Context, ImplRenderingParameters,
+ render_stability_since_raw_with_extra, AssocItemLink, AssocItemRender, Context,
+ ImplRenderingParameters, RenderMode,
};
use crate::clean;
use crate::config::ModuleSorting;
use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, Impl, RenderMode};
+use crate::formats::Impl;
use crate::html::escape::Escape;
use crate::html::format::{
display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space,
@@ -369,8 +368,8 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
if let (Some(a), Some(b)) = (s1, s2) {
match (a.is_stable(), b.is_stable()) {
(true, true) | (false, false) => {}
- (false, true) => return Ordering::Less,
- (true, false) => return Ordering::Greater,
+ (false, true) => return Ordering::Greater,
+ (true, false) => return Ordering::Less,
}
}
let lhs = i1.name.unwrap_or(kw::Empty);
@@ -429,7 +428,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
last_section = Some(my_section);
write!(
w,
- "<h2 id=\"{id}\" class=\"small-section-header\">\
+ "<h2 id=\"{id}\" class=\"section-header\">\
<a href=\"#{id}\">{name}</a>\
</h2>{ITEM_TABLE_OPEN}",
id = cx.derive_id(my_section.id()),
@@ -464,16 +463,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
clean::ImportItem(ref import) => {
let stab_tags = if let Some(import_def_id) = import.source.did {
- let ast_attrs = tcx.get_attrs_unchecked(import_def_id);
- let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs));
-
// Just need an item with the correct def_id and attrs
- let import_item = clean::Item {
- item_id: import_def_id.into(),
- attrs: import_attrs,
- cfg: ast_attrs.cfg(tcx, &cx.cache().hidden_cfg),
- ..myitem.clone()
- };
+ let import_item =
+ clean::Item { item_id: import_def_id.into(), ..myitem.clone() };
let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
stab_tags
@@ -596,8 +588,10 @@ fn extra_info_tags<'a, 'tcx: 'a>(
// 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)
+ if item
+ .stability(tcx)
+ .as_ref()
+ .is_some_and(|s| s.is_unstable() && s.feature != sym::rustc_private)
{
write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
}
@@ -824,7 +818,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
write!(
w,
- "<h2 id=\"{0}\" class=\"small-section-header\">\
+ "<h2 id=\"{0}\" class=\"section-header\">\
{1}<a href=\"#{0}\" class=\"anchor\">§</a>\
</h2>{2}",
id, title, extra_content
@@ -974,8 +968,9 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// if any Types with the same name but different DefId have been found.
let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
for implementor in implementors {
- if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cache) &&
- !did.is_local() {
+ if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cache)
+ && !did.is_local()
+ {
extern_crates.insert(did.krate);
}
match implementor.inner_impl().for_.without_borrowed_ref() {
@@ -1152,9 +1147,10 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
.take(cx.current.len())
.chain(std::iter::once("trait.impl"))
.collect();
- if let Some(did) = it.item_id.as_def_id() &&
- let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
- let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) {
+ if let Some(did) = it.item_id.as_def_id()
+ && let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) }
+ && let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern)
+ {
js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
} else {
@@ -1255,7 +1251,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
if let Some(inner_type) = &t.inner_type {
write!(
w,
- "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+ "<h2 id=\"aliased-type\" class=\"section-header\">\
Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
);
@@ -1499,10 +1495,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
s: &'a [clean::Item],
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(|f| {
- if s.iter()
- .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+ if !s.is_empty()
+ && s.iter().all(|field| {
+ matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+ })
{
- return f.write_str("/* private fields */");
+ return f.write_str("<span class=\"comment\">/* private fields */</span>");
}
for (i, ty) in s.iter().enumerate() {
@@ -1564,8 +1562,8 @@ fn should_show_enum_discriminant(
) -> bool {
let mut has_variants_with_value = false;
for variant in variants {
- if let clean::VariantItem(ref var) = *variant.kind &&
- matches!(var.kind, clean::VariantKind::CLike)
+ if let clean::VariantItem(ref var) = *variant.kind
+ && matches!(var.kind, clean::VariantKind::CLike)
{
has_variants_with_value |= var.discriminant.is_some();
} else {
@@ -1659,7 +1657,7 @@ fn render_enum_fields(
}
if variants_stripped && !is_non_exhaustive {
- w.write_str(" // some variants omitted\n");
+ w.write_str(" <span class=\"comment\">// some variants omitted</span>\n");
}
if toggle {
toggle_close(&mut w);
@@ -1678,7 +1676,7 @@ fn item_variants(
let tcx = cx.tcx();
write!(
w,
- "<h2 id=\"variants\" class=\"variants small-section-header\">\
+ "<h2 id=\"variants\" class=\"variants section-header\">\
Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
</h2>\
{}\
@@ -1706,8 +1704,8 @@ fn item_variants(
" rightside",
);
w.write_str("<h3 class=\"code-header\">");
- if let clean::VariantItem(ref var) = *variant.kind &&
- let clean::VariantKind::CLike = var.kind
+ if let clean::VariantItem(ref var) = *variant.kind
+ && let clean::VariantKind::CLike = var.kind
{
display_c_like_variant(
w,
@@ -1730,7 +1728,14 @@ fn item_variants(
w.write_str("</h3></section>");
let heading_and_fields = match &variant_data.kind {
- clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+ clean::VariantKind::Struct(s) => {
+ // If there is no field to display, no need to add the heading.
+ if s.fields.iter().any(|f| !f.is_doc_hidden()) {
+ Some(("Fields", &s.fields))
+ } else {
+ None
+ }
+ }
clean::VariantKind::Tuple(fields) => {
// Documentation on tuple variant fields is rare, so to reduce noise we only emit
// the section if at least one field is documented.
@@ -1765,7 +1770,7 @@ fn item_variants(
write!(
w,
"<div class=\"sub-variant-field\">\
- <span id=\"{id}\" class=\"small-section-header\">\
+ <span id=\"{id}\" class=\"section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}: {t}</code>\
</span>",
@@ -1804,7 +1809,8 @@ fn item_proc_macro(
let name = it.name.expect("proc-macros always have names");
match m.kind {
MacroKind::Bang => {
- write!(buffer, "{name}!() {{ /* proc-macro */ }}").unwrap();
+ write!(buffer, "{name}!() {{ <span class=\"comment\">/* proc-macro */</span> }}")
+ .unwrap();
}
MacroKind::Attr => {
write!(buffer, "#[{name}]").unwrap();
@@ -1812,7 +1818,12 @@ fn item_proc_macro(
MacroKind::Derive => {
write!(buffer, "#[derive({name})]").unwrap();
if !m.helpers.is_empty() {
- buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap();
+ buffer
+ .write_str(
+ "\n{\n \
+ <span class=\"comment\">// Attributes available to this derive:</span>\n",
+ )
+ .unwrap();
for attr in &m.helpers {
writeln!(buffer, " #[{attr}]").unwrap();
}
@@ -1922,7 +1933,7 @@ fn item_fields(
if fields.peek().is_some() {
write!(
w,
- "<h2 id=\"fields\" class=\"fields small-section-header\">\
+ "<h2 id=\"fields\" class=\"fields section-header\">\
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>\
{}",
@@ -1936,7 +1947,7 @@ fn item_fields(
let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
write!(
w,
- "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
+ "<span id=\"{id}\" class=\"{item_type} section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{field_name}: {ty}</code>\
</span>",
@@ -2174,7 +2185,7 @@ fn render_union<'a, 'cx: 'a>(
}
if it.has_stripped_entries().unwrap() {
- write!(f, " /* private fields */\n")?;
+ write!(f, " <span class=\"comment\">/* private fields */</span>\n")?;
}
if toggle {
toggle_close(&mut f);
@@ -2260,11 +2271,11 @@ fn render_struct_fields(
if has_visible_fields {
if has_stripped_entries {
- write!(w, "\n{tab} /* private fields */");
+ write!(w, "\n{tab} <span class=\"comment\">/* private fields */</span>");
}
write!(w, "\n{tab}");
} else if has_stripped_entries {
- write!(w, " /* private fields */ ");
+ write!(w, " <span class=\"comment\">/* private fields */</span> ");
}
if toggle {
toggle_close(&mut w);
@@ -2273,10 +2284,12 @@ fn render_struct_fields(
}
Some(CtorKind::Fn) => {
w.write_str("(");
- if fields.iter().all(|field| {
- matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
- }) {
- write!(w, "/* private fields */");
+ if !fields.is_empty()
+ && fields.iter().all(|field| {
+ matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+ })
+ {
+ write!(w, "<span class=\"comment\">/* private fields */</span>");
} else {
for (i, field) in fields.iter().enumerate() {
if i > 0 {
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index af1dab594..a1029320d 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -3,8 +3,10 @@ use std::collections::BTreeMap;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::DefId;
use rustc_span::symbol::Symbol;
use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
+use thin_vec::ThinVec;
use crate::clean;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -22,6 +24,7 @@ pub(crate) fn build_index<'tcx>(
) -> String {
let mut itemid_to_pathid = FxHashMap::default();
let mut primitives = FxHashMap::default();
+ let mut associated_types = FxHashMap::default();
let mut crate_paths = vec![];
// Attach all orphan items to the type's definition if the type
@@ -38,7 +41,13 @@ pub(crate) fn build_index<'tcx>(
parent: Some(parent),
parent_idx: None,
impl_id,
- search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
+ search_type: get_function_type_for_search(
+ item,
+ tcx,
+ impl_generics.as_ref(),
+ Some(parent),
+ cache,
+ ),
aliases: item.attrs.get_doc_aliases(),
deprecation: item.deprecation(tcx),
});
@@ -76,83 +85,139 @@ pub(crate) fn build_index<'tcx>(
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
for item in search_index.iter_mut() {
fn insert_into_map<F: std::hash::Hash + Eq>(
- ty: &mut RenderType,
map: &mut FxHashMap<F, isize>,
itemid: F,
lastpathid: &mut isize,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
path: &[Symbol],
- ) {
+ ) -> RenderTypeId {
match map.entry(itemid) {
- Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
+ Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
Entry::Vacant(entry) => {
let pathid = *lastpathid;
entry.insert(pathid);
*lastpathid += 1;
crate_paths.push((item_type, path.to_vec()));
- ty.id = Some(RenderTypeId::Index(pathid));
+ RenderTypeId::Index(pathid)
}
}
}
- fn convert_render_type(
- ty: &mut RenderType,
+ fn convert_render_type_id(
+ id: RenderTypeId,
cache: &mut Cache,
itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
primitives: &mut FxHashMap<Symbol, isize>,
+ associated_types: &mut FxHashMap<Symbol, isize>,
lastpathid: &mut isize,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
- ) {
- if let Some(generics) = &mut ty.generics {
- for item in generics {
- convert_render_type(
- item,
- cache,
- itemid_to_pathid,
- primitives,
- lastpathid,
- crate_paths,
- );
- }
- }
+ ) -> Option<RenderTypeId> {
let Cache { ref paths, ref external_paths, .. } = *cache;
- let Some(id) = ty.id.clone() else {
- assert!(ty.generics.is_some());
- return;
- };
match id {
RenderTypeId::DefId(defid) => {
if let Some(&(ref fqp, item_type)) =
paths.get(&defid).or_else(|| external_paths.get(&defid))
{
- insert_into_map(
- ty,
+ Some(insert_into_map(
itemid_to_pathid,
ItemId::DefId(defid),
lastpathid,
crate_paths,
item_type,
fqp,
- );
+ ))
} else {
- ty.id = None;
+ None
}
}
RenderTypeId::Primitive(primitive) => {
let sym = primitive.as_sym();
- insert_into_map(
- ty,
+ Some(insert_into_map(
primitives,
sym,
lastpathid,
crate_paths,
ItemType::Primitive,
&[sym],
+ ))
+ }
+ RenderTypeId::Index(_) => Some(id),
+ RenderTypeId::AssociatedType(sym) => Some(insert_into_map(
+ associated_types,
+ sym,
+ lastpathid,
+ crate_paths,
+ ItemType::AssocType,
+ &[sym],
+ )),
+ }
+ }
+
+ fn convert_render_type(
+ ty: &mut RenderType,
+ cache: &mut Cache,
+ itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+ primitives: &mut FxHashMap<Symbol, isize>,
+ associated_types: &mut FxHashMap<Symbol, isize>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
+ ) {
+ if let Some(generics) = &mut ty.generics {
+ for item in generics {
+ convert_render_type(
+ item,
+ cache,
+ itemid_to_pathid,
+ primitives,
+ associated_types,
+ lastpathid,
+ crate_paths,
);
}
- RenderTypeId::Index(_) => {}
}
+ if let Some(bindings) = &mut ty.bindings {
+ bindings.retain_mut(|(associated_type, constraints)| {
+ let converted_associated_type = convert_render_type_id(
+ *associated_type,
+ cache,
+ itemid_to_pathid,
+ primitives,
+ associated_types,
+ lastpathid,
+ crate_paths,
+ );
+ let Some(converted_associated_type) = converted_associated_type else {
+ return false;
+ };
+ *associated_type = converted_associated_type;
+ for constraint in constraints {
+ convert_render_type(
+ constraint,
+ cache,
+ itemid_to_pathid,
+ primitives,
+ associated_types,
+ lastpathid,
+ crate_paths,
+ );
+ }
+ true
+ });
+ }
+ let Some(id) = ty.id.clone() else {
+ assert!(ty.generics.is_some());
+ return;
+ };
+ ty.id = convert_render_type_id(
+ id,
+ cache,
+ itemid_to_pathid,
+ primitives,
+ associated_types,
+ lastpathid,
+ crate_paths,
+ );
}
if let Some(search_type) = &mut item.search_type {
for item in &mut search_type.inputs {
@@ -161,6 +226,7 @@ pub(crate) fn build_index<'tcx>(
cache,
&mut itemid_to_pathid,
&mut primitives,
+ &mut associated_types,
&mut lastpathid,
&mut crate_paths,
);
@@ -171,6 +237,7 @@ pub(crate) fn build_index<'tcx>(
cache,
&mut itemid_to_pathid,
&mut primitives,
+ &mut associated_types,
&mut lastpathid,
&mut crate_paths,
);
@@ -182,6 +249,7 @@ pub(crate) fn build_index<'tcx>(
cache,
&mut itemid_to_pathid,
&mut primitives,
+ &mut associated_types,
&mut lastpathid,
&mut crate_paths,
);
@@ -228,10 +296,11 @@ pub(crate) fn build_index<'tcx>(
let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default();
for &item in &crate_items {
- if item.impl_id.is_some() && let Some(parent_idx) = item.parent_idx {
- let count = associated_item_duplicates
- .entry((parent_idx, item.ty, item.name))
- .or_insert(0);
+ if item.impl_id.is_some()
+ && let Some(parent_idx) = item.parent_idx
+ {
+ let count =
+ associated_item_duplicates.entry((parent_idx, item.ty, item.name)).or_insert(0);
*count += 1;
}
}
@@ -419,7 +488,7 @@ pub(crate) fn build_index<'tcx>(
// Collect the index into a string
format!(
- r#""{}":{}"#,
+ r#"["{}",{}]"#,
krate.name(tcx),
serde_json::to_string(&CrateData {
doc: crate_doc,
@@ -441,12 +510,39 @@ pub(crate) fn get_function_type_for_search<'tcx>(
item: &clean::Item,
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
+ parent: Option<DefId>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
+ let mut trait_info = None;
+ let impl_or_trait_generics = impl_generics.or_else(|| {
+ if let Some(def_id) = parent
+ && let Some(trait_) = cache.traits.get(&def_id)
+ && let Some((path, _)) =
+ cache.paths.get(&def_id).or_else(|| cache.external_paths.get(&def_id))
+ {
+ let path = clean::Path {
+ res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id),
+ segments: path
+ .iter()
+ .map(|name| clean::PathSegment {
+ name: *name,
+ args: clean::GenericArgs::AngleBracketed {
+ args: Vec::new().into_boxed_slice(),
+ bindings: ThinVec::new(),
+ },
+ })
+ .collect(),
+ };
+ trait_info = Some((clean::Type::Path { path }, trait_.generics.clone()));
+ Some(trait_info.as_ref().unwrap())
+ } else {
+ None
+ }
+ });
let (mut inputs, mut output, where_clause) = match *item.kind {
- clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
- clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
- clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
+ clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) | clean::TyMethodItem(ref f) => {
+ get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache)
+ }
_ => return None,
};
@@ -456,14 +552,23 @@ pub(crate) fn get_function_type_for_search<'tcx>(
Some(IndexItemFunctionType { inputs, output, where_clause })
}
-fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
+fn get_index_type(
+ clean_type: &clean::Type,
+ generics: Vec<RenderType>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+) -> RenderType {
RenderType {
- id: get_index_type_id(clean_type),
+ id: get_index_type_id(clean_type, rgen),
generics: if generics.is_empty() { None } else { Some(generics) },
+ bindings: None,
}
}
-fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
+fn get_index_type_id(
+ clean_type: &clean::Type,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+) -> Option<RenderTypeId> {
+ use rustc_hir::def::{DefKind, Res};
match *clean_type {
clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())),
clean::DynTrait(ref bounds, _) => {
@@ -471,18 +576,27 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
}
clean::Primitive(p) => Some(RenderTypeId::Primitive(p)),
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
- get_index_type_id(type_)
+ get_index_type_id(type_, rgen)
}
// The type parameters are converted to generics in `simplify_fn_type`
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
+ clean::QPath(ref data) => {
+ if data.self_type.is_self_type()
+ && let Some(clean::Path { res: Res::Def(DefKind::Trait, trait_), .. }) = data.trait_
+ {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ let (idx, _) = rgen
+ .entry(SimplifiedParam::AssociatedType(trait_, data.assoc.name))
+ .or_insert_with(|| (idx, Vec::new()));
+ Some(RenderTypeId::Index(*idx))
+ } else {
+ None
+ }
+ }
// Not supported yet
- clean::BareFunction(_)
- | clean::Generic(_)
- | clean::ImplTrait(_)
- | clean::QPath { .. }
- | clean::Infer => None,
+ clean::BareFunction(_) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None,
}
}
@@ -492,6 +606,9 @@ enum SimplifiedParam {
Symbol(Symbol),
// every argument-position impl trait is its own type parameter
Anonymous(isize),
+ // in a trait definition, the associated types are all bound to
+ // their own type parameter
+ AssociatedType(DefId, Symbol),
}
/// The point of this function is to lower generics and types into the simplified form that the
@@ -522,10 +639,17 @@ fn simplify_fn_type<'tcx, 'a>(
}
// First, check if it's "Self".
+ let mut is_self = false;
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_,
+ Type::BorrowedRef { type_, .. } if type_.is_self_type() => {
+ is_self = true;
+ self_
+ }
+ type_ if type_.is_self_type() => {
+ is_self = true;
+ self_
+ }
arg => arg,
}
} else {
@@ -584,11 +708,19 @@ fn simplify_fn_type<'tcx, 'a>(
}
}
if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
- res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+ res.push(RenderType {
+ id: Some(RenderTypeId::Index(*idx)),
+ generics: None,
+ bindings: None,
+ });
} else {
let idx = -isize::try_from(rgen.len() + 1).unwrap();
rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
- res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ res.push(RenderType {
+ id: Some(RenderTypeId::Index(idx)),
+ generics: None,
+ bindings: None,
+ });
}
} else if let Type::ImplTrait(ref bounds) = *arg {
let mut type_bounds = Vec::new();
@@ -610,12 +742,16 @@ fn simplify_fn_type<'tcx, 'a>(
}
if is_return && !type_bounds.is_empty() {
// In parameter position, `impl Trait` is a unique thing.
- res.push(RenderType { id: None, generics: Some(type_bounds) });
+ res.push(RenderType { id: None, generics: Some(type_bounds), bindings: None });
} else {
// In parameter position, `impl Trait` is the same as an unnamed generic parameter.
let idx = -isize::try_from(rgen.len() + 1).unwrap();
rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
- res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ res.push(RenderType {
+ id: Some(RenderTypeId::Index(idx)),
+ generics: None,
+ bindings: None,
+ });
}
} else if let Type::Slice(ref ty) = *arg {
let mut ty_generics = Vec::new();
@@ -630,7 +766,7 @@ fn simplify_fn_type<'tcx, 'a>(
is_return,
cache,
);
- res.push(get_index_type(arg, ty_generics));
+ res.push(get_index_type(arg, ty_generics, rgen));
} else if let Type::Array(ref ty, _) = *arg {
let mut ty_generics = Vec::new();
simplify_fn_type(
@@ -644,7 +780,7 @@ fn simplify_fn_type<'tcx, 'a>(
is_return,
cache,
);
- res.push(get_index_type(arg, ty_generics));
+ res.push(get_index_type(arg, ty_generics, rgen));
} else if let Type::Tuple(ref tys) = *arg {
let mut ty_generics = Vec::new();
for ty in tys {
@@ -660,7 +796,7 @@ fn simplify_fn_type<'tcx, 'a>(
cache,
);
}
- res.push(get_index_type(arg, ty_generics));
+ res.push(get_index_type(arg, ty_generics, rgen));
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -668,12 +804,16 @@ fn simplify_fn_type<'tcx, 'a>(
// So in here, we can add it directly and look for its own type parameters (so for `Option`,
// we will look for them but not for `T`).
let mut ty_generics = Vec::new();
- if let Some(arg_generics) = arg.generics() {
- for gen in arg_generics.iter() {
+ let mut ty_bindings = Vec::new();
+ if let Some(arg_generics) = arg.generic_args() {
+ for ty in arg_generics.into_iter().filter_map(|gen| match gen {
+ clean::GenericArg::Type(ty) => Some(ty),
+ _ => None,
+ }) {
simplify_fn_type(
self_,
generics,
- gen,
+ &ty,
tcx,
recurse + 1,
&mut ty_generics,
@@ -682,17 +822,180 @@ fn simplify_fn_type<'tcx, 'a>(
cache,
);
}
+ for binding in arg_generics.bindings() {
+ simplify_fn_binding(
+ self_,
+ generics,
+ &binding,
+ tcx,
+ recurse + 1,
+ &mut ty_bindings,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ }
+ // Every trait associated type on self gets assigned to a type parameter index
+ // this same one is used later for any appearances of these types
+ //
+ // for example, Iterator::next is:
+ //
+ // trait Iterator {
+ // fn next(&mut self) -> Option<Self::Item>
+ // }
+ //
+ // Self is technically just Iterator, but we want to pretend it's more like this:
+ //
+ // fn next<T>(self: Iterator<Item=T>) -> Option<T>
+ if is_self
+ && let Type::Path { path } = arg
+ && let def_id = path.def_id()
+ && let Some(trait_) = cache.traits.get(&def_id)
+ && trait_.items.iter().any(|at| at.is_ty_associated_type())
+ {
+ for assoc_ty in &trait_.items {
+ if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind
+ && let Some(name) = assoc_ty.name
+ {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ let (idx, stored_bounds) = rgen
+ .entry(SimplifiedParam::AssociatedType(def_id, name))
+ .or_insert_with(|| (idx, Vec::new()));
+ let idx = *idx;
+ if stored_bounds.is_empty() {
+ // Can't just pass stored_bounds to simplify_fn_type,
+ // because it also accepts rgen as a parameter.
+ // Instead, have it fill in this local, then copy it into the map afterward.
+ let mut type_bounds = Vec::new();
+ for bound in bounds {
+ if let Some(path) = bound.get_trait_path() {
+ let ty = Type::Path { path };
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut type_bounds,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ }
+ let stored_bounds = &mut rgen
+ .get_mut(&SimplifiedParam::AssociatedType(def_id, name))
+ .unwrap()
+ .1;
+ if stored_bounds.is_empty() {
+ *stored_bounds = type_bounds;
+ }
+ }
+ ty_bindings.push((
+ RenderTypeId::AssociatedType(name),
+ vec![RenderType {
+ id: Some(RenderTypeId::Index(idx)),
+ generics: None,
+ bindings: None,
+ }],
+ ))
+ }
+ }
}
- let id = get_index_type_id(&arg);
+ let id = get_index_type_id(&arg, rgen);
if id.is_some() || !ty_generics.is_empty() {
res.push(RenderType {
id,
+ bindings: if ty_bindings.is_empty() { None } else { Some(ty_bindings) },
generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
});
}
}
}
+fn simplify_fn_binding<'tcx, 'a>(
+ self_: Option<&'a Type>,
+ generics: &Generics,
+ binding: &'a clean::TypeBinding,
+ tcx: TyCtxt<'tcx>,
+ recurse: usize,
+ res: &mut Vec<(RenderTypeId, Vec<RenderType>)>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+ is_return: bool,
+ cache: &Cache,
+) {
+ let mut ty_binding_constraints = Vec::new();
+ let ty_binding_assoc = RenderTypeId::AssociatedType(binding.assoc.name);
+ for gen in &binding.assoc.args {
+ match gen {
+ clean::GenericArg::Type(arg) => simplify_fn_type(
+ self_,
+ generics,
+ &arg,
+ tcx,
+ recurse + 1,
+ &mut ty_binding_constraints,
+ rgen,
+ is_return,
+ cache,
+ ),
+ clean::GenericArg::Lifetime(_)
+ | clean::GenericArg::Const(_)
+ | clean::GenericArg::Infer => {}
+ }
+ }
+ for binding in binding.assoc.args.bindings() {
+ simplify_fn_binding(
+ self_,
+ generics,
+ &binding,
+ tcx,
+ recurse + 1,
+ res,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ match &binding.kind {
+ clean::TypeBindingKind::Equality { term } => {
+ if let clean::Term::Type(arg) = &term {
+ simplify_fn_type(
+ self_,
+ generics,
+ arg,
+ tcx,
+ recurse + 1,
+ &mut ty_binding_constraints,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ }
+ clean::TypeBindingKind::Constraint { bounds } => {
+ for bound in &bounds[..] {
+ if let Some(path) = bound.get_trait_path() {
+ let ty = Type::Path { path };
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_binding_constraints,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ }
+ }
+ }
+ res.push((ty_binding_assoc, ty_binding_constraints));
+}
+
/// Return the full list of types when bounds have been resolved.
///
/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
@@ -700,13 +1003,15 @@ fn simplify_fn_type<'tcx, 'a>(
fn get_fn_inputs_and_outputs<'tcx>(
func: &Function,
tcx: TyCtxt<'tcx>,
- impl_generics: Option<&(clean::Type, clean::Generics)>,
+ impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
let decl = &func.decl;
+ let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
let combined_generics;
- let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
+ let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics {
match (impl_generics.is_empty(), func.generics.is_empty()) {
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
@@ -728,8 +1033,6 @@ fn get_fn_inputs_and_outputs<'tcx>(
(None, &func.generics)
};
- let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
-
let mut arg_types = Vec::new();
for arg in decl.inputs.values.iter() {
simplify_fn_type(
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index ba4aaaff5..3d28937eb 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -435,9 +435,9 @@ fn sidebar_deref_methods<'a>(
}
// 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| {
+ 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()
@@ -445,14 +445,7 @@ fn sidebar_deref_methods<'a>(
.unwrap_or(false)
})
{
- sidebar_deref_methods(
- cx,
- out,
- target_deref_impl,
- target_impls,
- derefs,
- used_links,
- );
+ sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs, used_links);
}
}
}
@@ -494,8 +487,13 @@ fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
&& it
.name
.or_else(|| {
- if let clean::ImportItem(ref i) = *it.kind &&
- let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+ if let clean::ImportItem(ref i) = *it.kind
+ && let clean::ImportKind::Simple(s) = i.kind
+ {
+ Some(s)
+ } else {
+ None
+ }
})
.is_some()
})
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 5f130f187..d1ece7337 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -94,7 +94,7 @@ impl<'tcx> SpanMapVisitor<'tcx> {
/// Used to generate links on items' definition to go to their documentation page.
pub(crate) fn extract_info_from_hir_id(&mut self, hir_id: HirId) {
- if let Some(Node::Item(item)) = self.tcx.hir().find(hir_id) {
+ if let Some(Node::Item(item)) = self.tcx.opt_hir_node(hir_id) {
if let Some(span) = self.tcx.def_ident_span(item.owner_id) {
let cspan = clean::Span::new(span);
// If the span isn't from the current crate, we ignore it.
@@ -177,7 +177,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
if !span.overlaps(m.spans.inner_span) {
// Now that we confirmed it's a file import, we want to get the span for the module
// name only and not all the "mod foo;".
- if let Some(Node::Item(item)) = self.tcx.hir().find(id) {
+ if let Some(Node::Item(item)) = self.tcx.opt_hir_node(id) {
self.matches.insert(
item.ident.span,
LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs
index 377daaeb9..738ea0aee 100644
--- a/src/librustdoc/html/render/type_layout.rs
+++ b/src/librustdoc/html/render/type_layout.rs
@@ -42,37 +42,35 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>(
let ty = tcx.type_of(ty_def_id).instantiate_identity();
let type_layout = tcx.layout_of(param_env.and(ty));
- let variants =
- if let Ok(type_layout) = type_layout &&
- let Variants::Multiple { variants, tag, tag_encoding, .. } =
- type_layout.layout.variants() &&
- !variants.is_empty()
- {
- 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")
- };
- variants
- .iter_enumerated()
- .map(|(variant_idx, variant_layout)| {
- let Adt(adt, _) = type_layout.ty.kind() else {
- span_bug!(tcx.def_span(ty_def_id), "not an adt")
- };
- let name = adt.variant(variant_idx).name;
- let is_unsized = variant_layout.abi.is_unsized();
- let is_uninhabited = variant_layout.abi.is_uninhabited();
- let size = variant_layout.size.bytes() - tag_size;
- let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
- (name, type_layout_size)
- })
- .collect()
+ let variants = if let Ok(type_layout) = type_layout
+ && let Variants::Multiple { variants, tag, tag_encoding, .. } =
+ type_layout.layout.variants()
+ && !variants.is_empty()
+ {
+ let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
+ 0
+ } else if let Primitive::Int(i, _) = tag.primitive() {
+ i.size().bytes()
} else {
- Vec::new()
+ span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
};
+ variants
+ .iter_enumerated()
+ .map(|(variant_idx, variant_layout)| {
+ let Adt(adt, _) = type_layout.ty.kind() else {
+ span_bug!(tcx.def_span(ty_def_id), "not an adt")
+ };
+ let name = adt.variant(variant_idx).name;
+ let is_unsized = variant_layout.abi.is_unsized();
+ let is_uninhabited = variant_layout.abi.is_uninhabited();
+ let size = variant_layout.size.bytes() - tag_size;
+ let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
+ (name, type_layout_size)
+ })
+ .collect()
+ } else {
+ Vec::new()
+ };
let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| {
let is_unsized = layout.abi.is_unsized();
@@ -81,6 +79,7 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>(
TypeLayoutSize { is_unsized, is_uninhabited, size }
});
- Ok(TypeLayout { variants, type_layout_size }.render_into(f).unwrap())
+ TypeLayout { variants, type_layout_size }.render_into(f).unwrap();
+ Ok(())
})
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index d2c7c578c..6408e97df 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -15,14 +15,14 @@ use rustc_span::Symbol;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
-use super::{collect_paths_for_type, ensure_trailing_slash, Context};
+use super::{collect_paths_for_type, ensure_trailing_slash, Context, RenderMode};
use crate::clean::{Crate, Item, ItemId, ItemKind};
use crate::config::{EmitType, RenderOptions};
use crate::docfs::PathError;
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{Impl, RenderMode};
+use crate::formats::Impl;
use crate::html::format::Buffer;
use crate::html::render::{AssocItemLink, ImplRenderingParameters};
use crate::html::{layout, static_files};
@@ -167,23 +167,24 @@ pub(super) fn write_shared(
let mut krates = Vec::new();
if path.exists() {
- let prefix = format!("\"{krate}\"");
+ let prefix = format!("[\"{krate}\"");
for line in BufReader::new(File::open(path)?).lines() {
let line = line?;
- if !line.starts_with('"') {
+ if !line.starts_with("[\"") {
continue;
}
if line.starts_with(&prefix) {
continue;
}
- if line.ends_with(",\\") {
+ if line.ends_with("],\\") {
ret.push(line[..line.len() - 2].to_string());
} else {
// Ends with "\\" (it's the case for the last added crate line)
ret.push(line[..line.len() - 1].to_string());
}
krates.push(
- line.split('"')
+ line[1..] // We skip the `[` parent at the beginning of the line.
+ .split('"')
.find(|s| !s.is_empty())
.map(|s| s.to_owned())
.unwrap_or_else(String::new),
@@ -285,7 +286,7 @@ pub(super) fn write_shared(
let (mut all_sources, _krates) =
try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst);
all_sources.push(format!(
- r#""{}":{}"#,
+ r#"["{}",{}]"#,
&krate.name(cx.tcx()),
hierarchy
.to_json_string()
@@ -296,9 +297,12 @@ pub(super) fn write_shared(
.replace("\\\"", "\\\\\"")
));
all_sources.sort();
- let mut v = String::from("var srcIndex = JSON.parse('{\\\n");
+ // This needs to be `var`, not `const`.
+ // This variable needs declared in the current global scope so that if
+ // src-script.js loads first, it can pick it up.
+ let mut v = String::from("var srcIndex = new Map(JSON.parse('[\\\n");
v.push_str(&all_sources.join(",\\\n"));
- v.push_str("\\\n}');\ncreateSrcSidebar();\n");
+ v.push_str("\\\n]'));\ncreateSrcSidebar();\n");
Ok(v.into_bytes())
};
write_invocation_specific("src-files.js", &make_sources)?;
@@ -316,13 +320,16 @@ pub(super) fn write_shared(
// with rustdoc running in parallel.
all_indexes.sort();
write_invocation_specific("search-index.js", &|| {
- let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
+ // This needs to be `var`, not `const`.
+ // This variable needs declared in the current global scope so that if
+ // search.js loads first, it can pick it up.
+ let mut v = String::from("var searchIndex = new Map(JSON.parse('[\\\n");
v.push_str(&all_indexes.join(",\\\n"));
v.push_str(
r#"\
-}');
-if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)};
-if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
+]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);
"#,
);
Ok(v.into_bytes())