summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:42 +0000
commitcec1877e180393eba0f6ddb0cf97bf3a791631c7 (patch)
tree47b4dac2a9dd9a40c30c251b4d4a72d7ccf77e9f /src/librustdoc/html
parentAdding debian version 1.74.1+dfsg1-1. (diff)
downloadrustc-cec1877e180393eba0f6ddb0cf97bf3a791631c7.tar.xz
rustc-cec1877e180393eba0f6ddb0cf97bf3a791631c7.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/html')
-rw-r--r--src/librustdoc/html/format.rs80
-rw-r--r--src/librustdoc/html/layout.rs37
-rw-r--r--src/librustdoc/html/markdown.rs5
-rw-r--r--src/librustdoc/html/render/context.rs75
-rw-r--r--src/librustdoc/html/render/mod.rs140
-rw-r--r--src/librustdoc/html/render/print_item.rs274
-rw-r--r--src/librustdoc/html/render/search_index.rs33
-rw-r--r--src/librustdoc/html/render/sidebar.rs105
-rw-r--r--src/librustdoc/html/render/write_shared.rs289
-rw-r--r--src/librustdoc/html/sources.rs7
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css87
-rw-r--r--src/librustdoc/html/static/js/main.js281
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--src/librustdoc/html/templates/page.html52
-rw-r--r--src/librustdoc/html/templates/sidebar.html7
15 files changed, 1200 insertions, 293 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2751b6613..29fd880af 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -325,8 +325,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
bounds_display.truncate(bounds_display.len() - " + ".len());
write!(f, "{}: {bounds_display}", lifetime.print())
}
- // FIXME(fmease): Render bound params.
- clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
+ clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
@@ -848,7 +847,7 @@ fn resolved_path<'cx>(
fn primitive_link(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
cx: &Context<'_>,
) -> fmt::Result {
primitive_link_fragment(f, prim, name, "", cx)
@@ -857,7 +856,7 @@ fn primitive_link(
fn primitive_link_fragment(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
fragment: &str,
cx: &Context<'_>,
) -> fmt::Result {
@@ -908,7 +907,7 @@ fn primitive_link_fragment(
None => {}
}
}
- f.write_str(name)?;
+ std::fmt::Display::fmt(&name, f)?;
if needs_termination {
write!(f, "</a>")?;
}
@@ -978,9 +977,11 @@ fn fmt_type<'cx>(
}
clean::Infer => write!(f, "_"),
clean::Primitive(clean::PrimitiveType::Never) => {
- primitive_link(f, PrimitiveType::Never, "!", cx)
+ primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
+ }
+ clean::Primitive(prim) => {
+ primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
}
- clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
clean::BareFunction(ref decl) => {
if f.alternate() {
write!(
@@ -999,16 +1000,16 @@ fn fmt_type<'cx>(
decl.unsafety.print_with_space(),
print_abi_with_space(decl.abi)
)?;
- primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
+ primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
write!(f, "{}", decl.decl.print(cx))
}
}
clean::Tuple(ref typs) => {
match &typs[..] {
- &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
+ &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
[one] => {
if let clean::Generic(name) = one {
- primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
+ primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
} else {
write!(f, "(")?;
// Carry `f.alternate()` into this display w/o branching manually.
@@ -1029,7 +1030,10 @@ fn fmt_type<'cx>(
primitive_link(
f,
PrimitiveType::Tuple,
- &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
+ format_args!(
+ "({})",
+ generic_names.iter().map(|s| s.as_str()).join(", ")
+ ),
cx,
)
} else {
@@ -1048,7 +1052,7 @@ fn fmt_type<'cx>(
}
clean::Slice(ref t) => match **t {
clean::Generic(name) => {
- primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
+ primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
}
_ => {
write!(f, "[")?;
@@ -1060,7 +1064,7 @@ fn fmt_type<'cx>(
clean::Generic(name) if !f.alternate() => primitive_link(
f,
PrimitiveType::Array,
- &format!("[{name}; {n}]", n = Escape(n)),
+ format_args!("[{name}; {n}]", n = Escape(n)),
cx,
),
_ => {
@@ -1070,7 +1074,12 @@ fn fmt_type<'cx>(
write!(f, "; {n}")?;
} else {
write!(f, "; ")?;
- primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::Array,
+ format_args!("{n}", n = Escape(n)),
+ cx,
+ )?;
}
write!(f, "]")
}
@@ -1082,22 +1091,32 @@ fn fmt_type<'cx>(
};
if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
- let text = if f.alternate() {
- format!("*{m} {ty:#}", ty = t.print(cx))
+ let ty = t.print(cx);
+ if f.alternate() {
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty:#}"),
+ cx,
+ )
} else {
- format!("*{m} {ty}", ty = t.print(cx))
- };
- primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty}"),
+ cx,
+ )
+ }
} else {
- primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{m} "), cx)?;
+ primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
fmt::Display::fmt(&t.print(cx), f)
}
}
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
- let lt = match l {
- Some(l) => format!("{} ", l.print()),
- _ => String::new(),
- };
+ let lt = display_fn(|f| match l {
+ Some(l) => write!(f, "{} ", l.print()),
+ _ => Ok(()),
+ });
let m = mutability.print_with_space();
let amp = if f.alternate() { "&" } else { "&amp;" };
@@ -1105,7 +1124,7 @@ fn fmt_type<'cx>(
return primitive_link(
f,
PrimitiveType::Reference,
- &format!("{amp}{lt}{m}{name}"),
+ format_args!("{amp}{lt}{m}{name}"),
cx,
);
}
@@ -1255,7 +1274,7 @@ impl clean::Impl {
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
} else if let clean::BareFunction(bare_fn) = &self.for_ &&
let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
(self.kind.is_fake_variadic() || self.kind.is_auto())
@@ -1282,7 +1301,7 @@ impl clean::Impl {
} else {
""
};
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
// Write output.
if !bare_fn.decl.output.is_unit() {
write!(f, " -> ")?;
@@ -1666,7 +1685,12 @@ impl clean::ImportSource {
}
let name = self.path.last();
if let hir::def::Res::PrimTy(p) = self.path.res {
- primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::from(p),
+ format_args!("{}", name.as_str()),
+ cx,
+ )?;
} else {
f.write_str(name.as_str())?;
}
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 8c5871d91..d4b4db0f3 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -17,6 +17,7 @@ pub(crate) struct Layout {
pub(crate) external_html: ExternalHtml,
pub(crate) default_settings: FxHashMap<String, String>,
pub(crate) krate: String,
+ pub(crate) krate_version: String,
/// The given user css file which allow to customize the generated
/// documentation theme.
pub(crate) css_file_extension: Option<PathBuf>,
@@ -31,6 +32,7 @@ pub(crate) struct Page<'a> {
pub(crate) static_root_path: Option<&'a str>,
pub(crate) description: &'a str,
pub(crate) resource_suffix: &'a str,
+ pub(crate) rust_logo: bool,
}
impl<'a> Page<'a> {
@@ -54,9 +56,19 @@ struct PageLayout<'a> {
themes: Vec<String>,
sidebar: String,
content: String,
- krate_with_trailing_slash: String,
rust_channel: &'static str,
pub(crate) rustdoc_version: &'a str,
+ // same as layout.krate, except on top-level pages like
+ // Settings, Help, All Crates, and About Scraped Examples,
+ // where these things instead give Rustdoc name and version.
+ //
+ // These are separate from the variables used for the search
+ // engine, because "Rustdoc" isn't necessarily a crate in
+ // the current workspace.
+ display_krate: &'a str,
+ display_krate_with_trailing_slash: String,
+ display_krate_version_number: &'a str,
+ display_krate_version_extra: &'a str,
}
pub(crate) fn render<T: Print, S: Print>(
@@ -66,12 +78,26 @@ pub(crate) fn render<T: Print, S: Print>(
t: T,
style_files: &[StylePath],
) -> String {
+ let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
+
+ let (display_krate, display_krate_version, display_krate_with_trailing_slash) =
+ if page.root_path == "./" {
+ // top level pages use Rust branding
+ ("Rustdoc", rustdoc_version, String::new())
+ } else {
+ let display_krate_with_trailing_slash =
+ ensure_trailing_slash(&layout.krate).to_string();
+ (&layout.krate[..], &layout.krate_version[..], display_krate_with_trailing_slash)
+ };
let static_root_path = page.get_static_root_path();
- let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
+
+ // bootstrap passes in parts of the version separated by tabs, but other stuff might use spaces
+ let (display_krate_version_number, display_krate_version_extra) =
+ display_krate_version.split_once([' ', '\t']).unwrap_or((display_krate_version, ""));
+
let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
themes.sort();
- let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
PageLayout {
@@ -82,7 +108,10 @@ pub(crate) fn render<T: Print, S: Print>(
themes,
sidebar,
content,
- krate_with_trailing_slash,
+ display_krate,
+ display_krate_with_trailing_slash,
+ display_krate_version_number,
+ display_krate_version_extra,
rust_channel: *crate::clean::utils::DOC_CHANNEL,
rustdoc_version,
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d24e6e5fa..2807dfed0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1750,7 +1750,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let span_for_offset_forward = |span: Range<usize>, open: u8, close: u8| {
@@ -1786,7 +1786,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
@@ -2024,6 +2024,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("required-associated-consts".into(), 1);
map.insert("required-methods".into(), 1);
map.insert("provided-methods".into(), 1);
+ map.insert("object-safety".into(), 1);
map.insert("implementors".into(), 1);
map.insert("synthetic-implementors".into(), 1);
map.insert("implementations-list".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 97714afaa..50777134d 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,13 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
-use rustc_span::source_map::FileName;
-use rustc_span::{sym, Symbol};
+use rustc_span::{sym, FileName, Symbol};
use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
@@ -24,13 +21,14 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
+use crate::clean::utils::has_doc_flag;
+use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{self, FormatRenderer};
+use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -149,53 +147,6 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}
-
- /// Returns a list of impls on the given type, and, if it's a type alias,
- /// other types that it aliases.
- pub(crate) fn all_impls_for_item<'a>(
- &'a self,
- it: &clean::Item,
- did: DefId,
- ) -> Vec<&'a formats::Impl> {
- let tcx = self.tcx;
- let cache = &self.cache;
- let mut saw_impls = FxHashSet::default();
- let mut v: Vec<&formats::Impl> = cache
- .impls
- .get(&did)
- .map(Vec::as_slice)
- .unwrap_or(&[])
- .iter()
- .filter(|i| saw_impls.insert(i.def_id()))
- .collect();
- if let TypeAliasItem(ait) = &*it.kind &&
- let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
- let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
- let Some(av) = cache.impls.get(&aliased_type_defid) &&
- let Some(alias_def_id) = it.item_id.as_def_id()
- {
- // This branch of the compiler compares types structually, but does
- // not check trait bounds. That's probably fine, since type aliases
- // don't normally constrain on them anyway.
- // https://github.com/rust-lang/rust/issues/21903
- //
- // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
- // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
- let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
- let reject_cx = DeepRejectCtxt {
- treat_obligation_params: TreatParams::AsCandidateKey,
- };
- v.extend(av.iter().filter(|impl_| {
- if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
- reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
- && saw_impls.insert(impl_def_id)
- } else {
- false
- }
- }));
- }
- v
- }
}
impl<'tcx> Context<'tcx> {
@@ -277,6 +228,7 @@ impl<'tcx> Context<'tcx> {
title: &title,
description: &desc,
resource_suffix: &clone_shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let mut page_buffer = Buffer::html();
print_item(self, it, &mut page_buffer, &page);
@@ -528,12 +480,14 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
if let Some(url) = playground_url {
playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
+ let krate_version = cache.crate_version.as_deref().unwrap_or_default();
let mut layout = layout::Layout {
logo: String::new(),
favicon: String::new(),
external_html,
default_settings,
krate: krate.name(tcx).to_string(),
+ krate_version: krate_version.to_string(),
css_file_extension: extension_css,
scrape_examples_extension: !call_locations.is_empty(),
};
@@ -658,21 +612,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let shared = Rc::clone(&self.shared);
let mut page = layout::Page {
title: "List of all items in this crate",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "../",
static_root_path: shared.static_root_path.as_deref(),
description: "List of all items in this crate",
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
let blocks = sidebar_module_like(all.item_sections());
let bar = Sidebar {
- title_prefix: "Crate ",
- title: crate_name.as_str(),
+ title_prefix: "",
+ title: "",
is_crate: false,
- version: "",
+ is_mod: false,
blocks: vec![blocks],
path: String::new(),
};
@@ -689,9 +644,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(final_file, v)?;
// Generating settings page.
- page.title = "Rustdoc settings";
+ page.title = "Settings";
page.description = "Settings of Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
@@ -739,9 +695,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(settings_file, v)?;
// Generating help page.
- page.title = "Rustdoc help";
+ page.title = "Help";
page.description = "Documentation for Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 3e671a64b..c52fa01bd 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -48,13 +48,13 @@ use std::str;
use std::string::ToString;
use askama::Template;
-use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
+use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
-use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::RustcVersion;
use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
@@ -102,6 +102,7 @@ pub(crate) struct IndexItem {
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
pub(crate) parent_idx: Option<isize>,
+ pub(crate) impl_id: Option<DefId>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -615,24 +616,22 @@ fn short_item_info(
) -> Vec<ShortItemInfo> {
let mut extra_info = vec![];
- if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
- item.deprecation(cx.tcx())
- {
+ if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
// We display deprecation messages for #[deprecated], but only display
// the future-deprecation messages for rustc versions.
- let mut message = if let Some(since) = since {
- let since = since.as_str();
- if !stability::deprecation_in_effect(&depr) {
- if since == "TBD" {
- String::from("Deprecating in a future Rust version")
+ let mut message = match since {
+ DeprecatedSince::RustcVersion(version) => {
+ if depr.is_in_effect() {
+ format!("Deprecated since {version}")
} else {
- format!("Deprecating in {}", Escape(since))
+ format!("Deprecating in {version}")
}
- } else {
- format!("Deprecated since {}", Escape(since))
}
- } else {
- String::from("Deprecated")
+ DeprecatedSince::Future => String::from("Deprecating in a future Rust version"),
+ DeprecatedSince::NonStandard(since) => {
+ format!("Deprecated since {}", Escape(since.as_str()))
+ }
+ DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
};
if let Some(note) = note {
@@ -867,10 +866,10 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
- write!(w, "{}", render_attributes_in_pre(meth, indent_str, tcx));
+ write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx));
(4, indent_str, Ending::NoNewline)
} else {
- render_attributes_in_code(w, meth, tcx);
+ render_attributes_in_code(w, meth, cx);
(0, "", Ending::Newline)
};
w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
@@ -910,13 +909,17 @@ fn assoc_method(
/// consequence of the above rules.
fn render_stability_since_raw_with_extra(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
extra_class: &str,
) -> bool {
- let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver);
+ let stable_version = if ver != containing_ver && let Some(ver) = &ver {
+ since_to_string(ver)
+ } else {
+ None
+ };
let mut title = String::new();
let mut stability = String::new();
@@ -930,7 +933,8 @@ fn render_stability_since_raw_with_extra(
Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. })
if Some(since) != containing_const_ver =>
{
- Some((format!("const since {since}"), format!("const: {since}")))
+ since_to_string(&since)
+ .map(|since| (format!("const since {since}"), format!("const: {since}")))
}
Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
let unstable = if let Some(n) = issue {
@@ -970,13 +974,21 @@ fn render_stability_since_raw_with_extra(
!stability.is_empty()
}
+fn since_to_string(since: &StableSince) -> Option<String> {
+ match since {
+ StableSince::Version(since) => Some(since.to_string()),
+ StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
+ StableSince::Err => None,
+ }
+}
+
#[inline]
fn render_stability_since_raw(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
) -> bool {
render_stability_since_raw_with_extra(
w,
@@ -1046,13 +1058,13 @@ fn render_assoc_item(
// When an attribute is rendered inside a `<pre>` tag, it is formatted using
// a whitespace prefix and newline.
-fn render_attributes_in_pre<'a, 'b: 'a>(
+fn render_attributes_in_pre<'a, 'tcx: 'a>(
it: &'a clean::Item,
prefix: &'a str,
- tcx: TyCtxt<'b>,
-) -> impl fmt::Display + Captures<'a> + Captures<'b> {
+ cx: &'a Context<'tcx>,
+) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
crate::html::format::display_fn(move |f| {
- for a in it.attributes(tcx, false) {
+ for a in it.attributes(cx.tcx(), cx.cache(), false) {
writeln!(f, "{prefix}{a}")?;
}
Ok(())
@@ -1061,8 +1073,8 @@ fn render_attributes_in_pre<'a, 'b: 'a>(
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
-fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) {
- for attr in it.attributes(tcx, false) {
+fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
+ for attr in it.attributes(cx.tcx(), cx.cache(), false) {
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
}
}
@@ -1131,13 +1143,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(did);
+ derefs.insert(it);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
})
}
@@ -1146,17 +1158,15 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- let v = shared.all_impls_for_item(containing_item, did);
- let v = v.as_slice();
- let (non_trait, traits): (Vec<&Impl>, _) =
- v.iter().partition(|i| i.inner_impl().trait_.is_none());
- let mut saw_impls = FxHashSet::default();
+ let cache = &shared.cache;
+ 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::html();
let (render_mode, id, class_html) = match what {
@@ -1185,9 +1195,6 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
- if !saw_impls.insert(i.def_id()) {
- continue;
- }
render_impl(
&mut impls_buf,
cx,
@@ -1233,10 +1240,8 @@ fn render_assoc_items_inner(
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
- .into_iter()
- .filter(|t| saw_impls.insert(t.def_id()))
- .partition(|t| t.inner_impl().kind.is_blanket());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
+ concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1877,7 +1882,7 @@ pub(crate) fn render_impl_summary(
aliases: &[String],
) {
let inner_impl = i.inner_impl();
- let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
+ let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
let aliases = if aliases.is_empty() {
String::new()
} else {
@@ -1994,21 +1999,35 @@ pub(crate) fn small_url_encode(s: String) -> String {
}
}
-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))),
- None => small_url_encode(format!("impl-{:#}", for_.print(cx))),
- }
+fn get_id_for_impl<'tcx>(tcx: TyCtxt<'tcx>, impl_id: ItemId) -> String {
+ use rustc_middle::ty::print::with_forced_trimmed_paths;
+ let (type_, trait_) = match impl_id {
+ ItemId::Auto { trait_, for_ } => {
+ let ty = tcx.type_of(for_).skip_binder();
+ (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
+ }
+ ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
+ match tcx.impl_subject(impl_id).skip_binder() {
+ ty::ImplSubject::Trait(trait_ref) => {
+ (trait_ref.args[0].expect_ty(), Some(trait_ref))
+ }
+ ty::ImplSubject::Inherent(ty) => (ty, None),
+ }
+ }
+ };
+ with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
+ format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
+ } else {
+ format!("impl-{type_}")
+ }))
}
fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
match *item.kind {
- clean::ItemKind::ImplItem(ref i) => {
- i.trait_.as_ref().map(|trait_| {
- // Alternative format produces no URLs,
- // so this parameter does nothing.
- (format!("{:#}", i.for_.print(cx)), get_id_for_impl(&i.for_, Some(trait_), cx))
- })
+ clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
+ // Alternative format produces no URLs,
+ // so this parameter does nothing.
+ Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
}
_ => None,
}
@@ -2079,6 +2098,7 @@ impl ItemSection {
const ALL: &'static [Self] = {
use ItemSection::*;
// NOTE: The order here affects the order in the UI.
+ // Keep this synchronized with addSidebarItems in main.js
&[
Reexports,
PrimitiveTypes,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index c6751c958..d226701ba 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -5,10 +5,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
-use rustc_middle::middle::stability;
+use rustc_index::IndexVec;
+use rustc_middle::query::Key;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_target::abi::VariantIdx;
use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::fmt;
@@ -117,8 +119,7 @@ macro_rules! item_template_methods {
fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
display_fn(move |f| {
let (item, cx) = self.item_and_mut_cx();
- let tcx = cx.tcx();
- let v = render_attributes_in_pre(item, "", tcx);
+ let v = render_attributes_in_pre(item, "", &cx);
write!(f, "{v}")
})
}
@@ -589,11 +590,7 @@ fn extra_info_tags<'a, 'tcx: 'a>(
// 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"
- };
+ let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
write!(f, "{}", tag_html("deprecated", "", message))?;
}
@@ -656,7 +653,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
w,
"{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -691,7 +688,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"{attrs}{vis}{unsafety}{is_auto}trait {name}{generics}{bounds}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
unsafety = t.unsafety(tcx).print_with_space(),
is_auto = if t.is_auto(tcx) { "auto " } else { "" },
@@ -957,6 +954,21 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
let mut extern_crates = FxHashSet::default();
+
+ if !t.is_object_safe(cx.tcx()) {
+ write_small_section_header(
+ w,
+ "object-safety",
+ "Object Safety",
+ &format!(
+ "<div class=\"object-safety-info\">This trait is <b>not</b> \
+ <a href=\"{base}/reference/items/traits.html#object-safety\">\
+ object safe</a>.</div>",
+ base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
+ ),
+ );
+ }
+
if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) {
// The DefId is for the first Type found with that name. The bool is
// if any Types with the same name but different DefId have been found.
@@ -979,7 +991,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
- let (local, foreign) =
+ let (local, mut foreign) =
implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
@@ -987,6 +999,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
+ foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
@@ -1064,6 +1077,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
+ // [RUSTDOCIMPL] trait.impl
+ //
// Include implementors in crates that depend on the current crate.
//
// This is complicated by the way rustdoc is invoked, which is basically
@@ -1099,7 +1114,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// ```
//
// Basically, we want `C::Baz` and `A::Foo` to show the same set of
- // impls, which is easier if they both treat `/implementors/A/trait.Foo.js`
+ // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
// as the Single Source of Truth.
//
// We also want the `impl Baz for Quux` to be written to
@@ -1108,7 +1123,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// because that'll load faster, and it's better for SEO. And we don't want
// the same impl to show up twice on the same page.
//
- // To make this work, the implementors JS file has a structure kinda
+ // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
// like this:
//
// ```js
@@ -1125,7 +1140,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// So C's HTML will have something like this:
//
// ```html
- // <script src="/implementors/A/trait.Foo.js"
+ // <script src="/trait.impl/A/trait.Foo.js"
// data-ignore-extern-crates="A,B" async></script>
// ```
//
@@ -1135,7 +1150,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// [JSONP]: https://en.wikipedia.org/wiki/JSONP
let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
.take(cx.current.len())
- .chain(std::iter::once("implementors"))
+ .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) } &&
@@ -1170,7 +1185,7 @@ fn item_trait_alias(
write!(
w,
"{attrs}trait {name}{generics}{where_b} = {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1198,7 +1213,7 @@ fn item_opaque_ty(
write!(
w,
"{attrs}type {name}{generics}{where_clause} = impl {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1223,7 +1238,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
write!(
w,
"{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
@@ -1247,6 +1262,9 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
+ let enum_def_id = ty.ty_adt_id().unwrap();
+
wrap_item(w, |w| {
let variants_len = variants.len();
let variants_count = variants_iter().count();
@@ -1257,13 +1275,14 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
w,
cx,
Some(&t.generics),
- variants_iter(),
+ &variants,
variants_count,
has_stripped_entries,
*is_non_exhaustive,
+ enum_def_id,
)
});
- item_variants(w, cx, it, variants_iter());
+ item_variants(w, cx, it, &variants, enum_def_id);
}
clean::TypeAliasInnerType::Union { fields } => {
wrap_item(w, |w| {
@@ -1313,6 +1332,102 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
// we need #14072 to make sense of the generics.
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
+
+ // [RUSTDOCIMPL] type.impl
+ //
+ // Include type definitions from the alias target type.
+ //
+ // Earlier versions of this code worked by having `render_assoc_items`
+ // include this data directly. That generates *O*`(types*impls)` of HTML
+ // text, and some real crates have a lot of types and impls.
+ //
+ // To create the same UX without generating half a gigabyte of HTML for a
+ // crate that only contains 20 megabytes of actual documentation[^115718],
+ // rustdoc stashes these type-alias-inlined docs in a [JSONP]
+ // "database-lite". The file itself is generated in `write_shared.rs`,
+ // and hooks into functions provided by `main.js`.
+ //
+ // The format of `trait.impl` and `type.impl` JS files are superficially
+ // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
+ // and they are otherwise separate (rustdoc should be idempotent). The
+ // "meat" of the file is HTML strings, so the frontend code is very simple.
+ // Links are relative to the doc root, though, so the frontend needs to fix
+ // that up, and inlined docs can reuse these files.
+ //
+ // However, there are a few differences, caused by the sophisticated
+ // features that type aliases have. Consider this crate graph:
+ //
+ // ```text
+ // ---------------------------------
+ // | crate A: struct Foo<T> |
+ // | type Bar = Foo<i32> |
+ // | impl X for Foo<i8> |
+ // | impl Y for Foo<i32> |
+ // ---------------------------------
+ // |
+ // ----------------------------------
+ // | crate B: type Baz = A::Foo<i8> |
+ // | type Xyy = A::Foo<i8> |
+ // | impl Z for Xyy |
+ // ----------------------------------
+ // ```
+ //
+ // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
+ //
+ // ```js
+ // JSONP({
+ // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
+ // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
+ // });
+ // ```
+ //
+ // When the type.impl file is loaded, only the current crate's docs are
+ // actually used. The main reason to bundle them together is that there's
+ // enough duplication in them for DEFLATE to remove the redundancy.
+ //
+ // The contents of a crate are a list of impl blocks, themselves
+ // represented as lists. The first item in the sublist is the HTML block,
+ // the second item is the name of the trait (which goes in the sidebar),
+ // and all others are the names of type aliases that successfully match.
+ //
+ // This way:
+ //
+ // - There's no need to generate these files for types that have no aliases
+ // in the current crate. If a dependent crate makes a type alias, it'll
+ // take care of generating its own docs.
+ // - There's no need to reimplement parts of the type checker in
+ // JavaScript. The Rust backend does the checking, and includes its
+ // results in the file.
+ // - Docs defined directly on the type alias are dropped directly in the
+ // HTML by `render_assoc_items`, and are accessible without JavaScript.
+ // The JSONP file will not list impl items that are known to be part
+ // of the main HTML file already.
+ //
+ // [JSONP]: https://en.wikipedia.org/wiki/JSONP
+ // [^115718]: https://github.com/rust-lang/rust/issues/115718
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+ if let Some(target_did) = t.type_.def_id(cache) &&
+ let get_extern = { || cache.external_paths.get(&target_did) } &&
+ let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) &&
+ target_type.is_adt() && // primitives cannot be inlined
+ let Some(self_did) = it.item_id.as_def_id() &&
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } &&
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
+ {
+ let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
+ .take(cx.current.len())
+ .chain(std::iter::once("type.impl"))
+ .collect();
+ js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
+ js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
+ let self_path = self_fqp.iter().map(Symbol::as_str).collect::<Vec<&str>>().join("::");
+ write!(
+ w,
+ "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
+ src = js_src_path.finish(),
+ );
+ }
}
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
@@ -1408,7 +1523,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_item(w, |w| {
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
"{}enum {}{}",
@@ -1416,36 +1531,90 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
+
render_enum_fields(
w,
cx,
Some(&e.generics),
- e.variants(),
+ &e.variants,
count_variants,
e.has_stripped_entries(),
it.is_non_exhaustive(),
+ it.def_id().unwrap(),
);
});
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if count_variants != 0 {
- item_variants(w, cx, it, e.variants());
+ item_variants(w, cx, it, &e.variants, it.def_id().unwrap());
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}
-fn render_enum_fields<'a>(
+/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
+/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
+/// `repr`.
+fn should_show_enum_discriminant(
+ cx: &Context<'_>,
+ enum_def_id: DefId,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+) -> 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)
+ {
+ has_variants_with_value |= var.discriminant.is_some();
+ } else {
+ return false;
+ }
+ }
+ if has_variants_with_value {
+ return true;
+ }
+ let repr = cx.tcx().adt_def(enum_def_id).repr();
+ repr.c() || repr.int.is_some()
+}
+
+fn display_c_like_variant(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ item: &clean::Item,
+ variant: &clean::Variant,
+ index: VariantIdx,
+ should_show_enum_discriminant: bool,
+ enum_def_id: DefId,
+) {
+ let name = item.name.unwrap();
+ if let Some(ref value) = variant.discriminant {
+ write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
+ } else if should_show_enum_discriminant {
+ let adt_def = cx.tcx().adt_def(enum_def_id);
+ let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
+ if discr.ty.is_signed() {
+ write!(w, "{} = {}", name.as_str(), discr.val as i128);
+ } else {
+ write!(w, "{} = {}", name.as_str(), discr.val);
+ }
+ } else {
+ w.write_str(name.as_str());
+ }
+}
+
+fn render_enum_fields(
mut w: &mut Buffer,
cx: &mut Context<'_>,
g: Option<&clean::Generics>,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
count_variants: usize,
has_stripped_entries: bool,
is_non_exhaustive: bool,
+ enum_def_id: DefId,
) {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
// If there wasn't a `where` clause, we add a whitespace.
w.write_str(" ");
@@ -1461,15 +1630,24 @@ fn render_enum_fields<'a>(
toggle_open(&mut w, format_args!("{count_variants} variants"));
}
const TAB: &str = " ";
- for v in variants {
+ for (index, v) in variants.iter_enumerated() {
+ if v.is_stripped() {
+ continue;
+ }
w.write_str(TAB);
- let name = v.name.unwrap();
match *v.kind {
- // FIXME(#101337): Show discriminant
clean::VariantItem(ref var) => match var.kind {
- clean::VariantKind::CLike => w.write_str(name.as_str()),
+ clean::VariantKind::CLike => display_c_like_variant(
+ w,
+ cx,
+ v,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ ),
clean::VariantKind::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
}
clean::VariantKind::Struct(ref s) => {
render_struct(w, v, None, None, &s.fields, TAB, false, cx);
@@ -1490,11 +1668,12 @@ fn render_enum_fields<'a>(
}
}
-fn item_variants<'a>(
+fn item_variants(
w: &mut Buffer,
cx: &mut Context<'_>,
it: &clean::Item,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+ enum_def_id: DefId,
) {
let tcx = cx.tcx();
write!(
@@ -1507,7 +1686,11 @@ fn item_variants<'a>(
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
- for variant in variants {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
+ for (index, variant) in variants.iter_enumerated() {
+ if variant.is_stripped() {
+ continue;
+ }
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
@@ -1522,7 +1705,22 @@ fn item_variants<'a>(
it.const_stable_since(tcx),
" rightside",
);
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ w.write_str("<h3 class=\"code-header\">");
+ if let clean::VariantItem(ref var) = *variant.kind &&
+ let clean::VariantKind::CLike = var.kind
+ {
+ display_c_like_variant(
+ w,
+ cx,
+ variant,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ );
+ } else {
+ w.write_str(variant.name.unwrap().as_str());
+ }
let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
@@ -1644,7 +1842,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_item(w, |w| {
let tcx = cx.tcx();
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
@@ -1693,7 +1891,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_item(w, |w| {
- render_attributes_in_code(w, it, cx.tcx());
+ render_attributes_in_code(w, it, cx);
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
@@ -1753,7 +1951,7 @@ fn item_fields(
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
wrap_item(w, |buffer| {
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
"{vis}static {mutability}{name}: {typ}",
@@ -1771,7 +1969,7 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item,
fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) {
wrap_item(w, |buffer| {
buffer.write_str("extern {\n").unwrap();
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
" {}type {};\n}}",
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 78c443b22..af1dab594 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -12,7 +12,7 @@ use crate::formats::cache::{Cache, OrphanImplItem};
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
-use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
+use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
/// Builds the search index from the collected metadata
pub(crate) fn build_index<'tcx>(
@@ -26,7 +26,8 @@ pub(crate) fn build_index<'tcx>(
// Attach all orphan items to the type's definition if the type
// has since been learned.
- for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
+ for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items
+ {
if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
cache.search_index.push(IndexItem {
@@ -36,6 +37,7 @@ pub(crate) fn build_index<'tcx>(
desc,
parent: Some(parent),
parent_idx: None,
+ impl_id,
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
aliases: item.attrs.get_doc_aliases(),
deprecation: item.deprecation(tcx),
@@ -222,6 +224,29 @@ pub(crate) fn build_index<'tcx>(
})
.collect();
+ // Find associated items that need disambiguators
+ 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);
+ *count += 1;
+ }
+ }
+
+ let associated_item_disambiguators = crate_items
+ .iter()
+ .enumerate()
+ .filter_map(|(index, item)| {
+ let impl_id = ItemId::DefId(item.impl_id?);
+ let parent_idx = item.parent_idx?;
+ let count = *associated_item_duplicates.get(&(parent_idx, item.ty, item.name))?;
+ if count > 1 { Some((index, render::get_id_for_impl(tcx, impl_id))) } else { None }
+ })
+ .collect::<Vec<_>>();
+
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
@@ -230,6 +255,8 @@ pub(crate) fn build_index<'tcx>(
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
+ // Used when a type has more than one impl with an associated item with the same name.
+ associated_item_disambiguators: &'a Vec<(usize, String)>,
}
struct Paths {
@@ -382,6 +409,7 @@ pub(crate) fn build_index<'tcx>(
crate_data.serialize_field("f", &functions)?;
crate_data.serialize_field("c", &deprecated)?;
crate_data.serialize_field("p", &paths)?;
+ crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -398,6 +426,7 @@ pub(crate) fn build_index<'tcx>(
items: crate_items,
paths: crate_paths,
aliases: &aliases,
+ associated_item_disambiguators: &associated_item_disambiguators,
})
.expect("failed serde conversion")
// All these `replace` calls are because we have to go through JS string for JSON content.
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 76f63c6f6..ba4aaaff5 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -19,7 +19,7 @@ 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) is_mod: bool,
pub(super) blocks: Vec<LinkBlock<'a>>,
pub(super) path: String,
}
@@ -38,18 +38,19 @@ pub(crate) struct LinkBlock<'a> {
/// as well as the link to it, e.g. `#implementations`.
/// Will be rendered inside an `<h3>` tag
heading: Link<'a>,
+ class: &'static str,
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 new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self {
+ Self { heading, links, class, force_render: false }
}
- pub fn forced(heading: Link<'a>) -> Self {
- Self { heading, links: vec![], force_render: true }
+ pub fn forced(heading: Link<'a>, class: &'static str) -> Self {
+ Self { heading, links: vec![], class, force_render: true }
}
pub fn should_render(&self) -> bool {
@@ -99,12 +100,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_primitive()
|| it.is_union()
|| it.is_enum()
- || it.is_mod()
+ // crate title is displayed as part of logo lockup
+ || (it.is_mod() && !it.is_crate())
|| it.is_type_alias()
{
(
match *it.kind {
- clean::ModuleItem(..) if it.is_crate() => "Crate ",
clean::ModuleItem(..) => "Module ",
_ => "",
},
@@ -113,14 +114,22 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
} 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()
+ // need to show parent path header if:
+ // - it's a child module, instead of the crate root
+ // - there's a sidebar section for the item itself
+ //
+ // otherwise, the parent path header is redundant with the big crate
+ // branding area at the top of the sidebar
+ let sidebar_path =
+ if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
+ let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
+ let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
+ if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
} else {
"".into()
};
- let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+ let sidebar =
+ Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path };
sidebar.render_into(buffer).unwrap();
}
@@ -149,7 +158,7 @@ fn sidebar_struct<'a>(
};
let mut items = vec![];
if let Some(name) = field_name {
- items.push(LinkBlock::new(Link::new("fields", name), fields));
+ items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
}
sidebar_assoc_items(cx, it, &mut items);
items
@@ -206,12 +215,23 @@ fn sidebar_trait<'a>(
("foreign-impls", "Implementations on Foreign Types", foreign_impls),
]
.into_iter()
- .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+ .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_object_safe(cx.tcx()) {
+ blocks.push(LinkBlock::forced(
+ Link::new("object-safety", "Object Safety"),
+ "object-safety-note",
+ ));
+ }
+
+ blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
if t.is_auto(cx.tcx()) {
- blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+ blocks.push(LinkBlock::forced(
+ Link::new("synthetic-implementors", "Auto Implementors"),
+ "impl-auto",
+ ));
}
blocks
}
@@ -237,7 +257,7 @@ fn sidebar_type_alias<'a>(
) -> Vec<LinkBlock<'a>> {
let mut items = vec![];
if let Some(inner_type) = &t.inner_type {
- items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+ items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type"));
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
let mut variants = variants
@@ -248,12 +268,12 @@ fn sidebar_type_alias<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
}
clean::TypeAliasInnerType::Union { fields }
| clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
let fields = get_struct_fields_name(fields);
- items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+ items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
}
}
}
@@ -267,7 +287,7 @@ fn sidebar_union<'a>(
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)];
+ let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -279,12 +299,11 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
- let v = v.as_slice();
+ let cache = cx.cache();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if !v.is_empty() {
+ if let Some(v) = cache.impls.get(&did) {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -320,7 +339,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v.iter().copied(),
+ v,
&mut derefs,
&mut used_links,
);
@@ -333,12 +352,16 @@ fn sidebar_assoc_items<'a>(
sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
} else {
- std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+ 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),
+ LinkBlock::new(
+ Link::new("implementations", "Associated Constants"),
+ "associatedconstant",
+ assoc_consts,
+ ),
+ LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
];
blocks.append(&mut deref_methods);
blocks.extend([concrete, synthetic, blanket]);
@@ -350,7 +373,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: impl Iterator<Item = &'a Impl>,
+ v: &[Impl],
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -375,7 +398,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ 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(|| {
@@ -407,7 +430,7 @@ fn sidebar_deref_methods<'a>(
);
// 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));
+ out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
}
}
@@ -426,7 +449,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls.iter(),
+ target_impls,
derefs,
used_links,
);
@@ -446,7 +469,7 @@ fn sidebar_enum<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+ let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -460,7 +483,7 @@ pub(crate) fn sidebar_module_like(
.filter(|sec| item_sections_in_use.contains(sec))
.map(|sec| Link::new(sec.id(), sec.name()))
.collect();
- LinkBlock::new(Link::empty(), item_sections)
+ LinkBlock::new(Link::empty(), "", item_sections)
}
fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
@@ -503,8 +526,7 @@ fn sidebar_render_assoc_items(
.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 encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
let prefix = match it.inner_impl().polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
@@ -522,12 +544,21 @@ fn sidebar_render_assoc_items(
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("trait-implementations", "Trait Implementations"),
+ "trait-implementation",
+ concrete,
+ ),
LinkBlock::new(
Link::new("synthetic-implementations", "Auto Trait Implementations"),
+ "synthetic-implementation",
synthetic,
),
- LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+ LinkBlock::new(
+ Link::new("blanket-implementations", "Blanket Implementations"),
+ "blanket-implementation",
+ blanket,
+ ),
]
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index e824651e7..d2c7c578c 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -5,18 +5,28 @@ use std::io::{self, BufReader};
use std::path::{Component, Path};
use std::rc::{Rc, Weak};
+use indexmap::IndexMap;
use itertools::Itertools;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_span::def_id::DefId;
+use rustc_span::Symbol;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use super::{collect_paths_for_type, ensure_trailing_slash, Context};
-use crate::clean::Crate;
+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::html::format::Buffer;
+use crate::html::render::{AssocItemLink, ImplRenderingParameters};
use crate::html::{layout, static_files};
+use crate::visit::DocVisitor;
use crate::{try_err, try_none};
/// Rustdoc writes out two kinds of shared files:
@@ -336,33 +346,286 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
let dst = cx.dst.join("index.html");
let page = layout::Page {
title: "Index of crates",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "./",
static_root_path: shared.static_root_path.as_deref(),
description: "List of crates",
resource_suffix: &shared.resource_suffix,
+ rust_logo: true,
};
let content = format!(
"<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
- krates
- .iter()
- .map(|s| {
- format!(
- "<li><a href=\"{trailing_slash}index.html\">{s}</a></li>",
- trailing_slash = ensure_trailing_slash(s),
- )
- })
- .collect::<String>()
+ krates.iter().format_with("", |k, f| {
+ f(&format_args!(
+ "<li><a href=\"{trailing_slash}index.html\">{k}</a></li>",
+ trailing_slash = ensure_trailing_slash(k),
+ ))
+ })
);
let v = layout::render(&shared.layout, &page, "", content, &shared.style_files);
shared.fs.write(dst, v)?;
}
}
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+
+ // Collect the list of aliased types and their aliases.
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ //
+ // The clean AST has type aliases that point at their types, but
+ // this visitor works to reverse that: `aliased_types` is a map
+ // from target to the aliases that reference it, and each one
+ // will generate one file.
+ struct TypeImplCollector<'cx, 'cache> {
+ // Map from DefId-of-aliased-type to its data.
+ aliased_types: IndexMap<DefId, AliasedType<'cache>>,
+ visited_aliases: FxHashSet<DefId>,
+ cache: &'cache Cache,
+ cx: &'cache mut Context<'cx>,
+ }
+ // Data for an aliased type.
+ //
+ // In the final file, the format will be roughly:
+ //
+ // ```json
+ // // type.impl/CRATE/TYPENAME.js
+ // JSONP(
+ // "CRATE": [
+ // ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...],
+ // ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...],
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType
+ // ...
+ // ]
+ // )
+ // ```
+ struct AliasedType<'cache> {
+ // This is used to generate the actual filename of this aliased type.
+ target_fqp: &'cache [Symbol],
+ target_type: ItemType,
+ // This is the data stored inside the file.
+ // ItemId is used to deduplicate impls.
+ impl_: IndexMap<ItemId, AliasedTypeImpl<'cache>>,
+ }
+ // The `impl_` contains data that's used to figure out if an alias will work,
+ // and to generate the HTML at the end.
+ //
+ // The `type_aliases` list is built up with each type alias that matches.
+ struct AliasedTypeImpl<'cache> {
+ impl_: &'cache Impl,
+ type_aliases: Vec<(&'cache [Symbol], Item)>,
+ }
+ impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> {
+ fn visit_item(&mut self, it: &Item) {
+ self.visit_item_recur(it);
+ let cache = self.cache;
+ let ItemKind::TypeAliasItem(ref t) = *it.kind else { return };
+ let Some(self_did) = it.item_id.as_def_id() else { return };
+ if !self.visited_aliases.insert(self_did) {
+ return;
+ }
+ let Some(target_did) = t.type_.def_id(cache) else { return };
+ let get_extern = { || cache.external_paths.get(&target_did) };
+ let Some(&(ref target_fqp, target_type)) =
+ cache.paths.get(&target_did).or_else(get_extern)
+ else {
+ return;
+ };
+ let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
+ let impl_ = cache
+ .impls
+ .get(&target_did)
+ .map(|v| &v[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|impl_| {
+ (
+ impl_.impl_item.item_id,
+ AliasedTypeImpl { impl_, type_aliases: Vec::new() },
+ )
+ })
+ .collect();
+ AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
+ });
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
+ return;
+ };
+ let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
+ // Exclude impls that are directly on this type. They're already in the HTML.
+ // Some inlining scenarios can cause there to be two versions of the same
+ // impl: one on the type alias and one on the underlying target type.
+ let mut seen_impls: FxHashSet<ItemId> = cache
+ .impls
+ .get(&self_did)
+ .map(|s| &s[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|i| i.impl_item.item_id)
+ .collect();
+ for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
+ // Only include this impl if it actually unifies with this alias.
+ // Synthetic impls are not included; those are also included in the HTML.
+ //
+ // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this
+ // to use type unification.
+ // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
+ let Some(impl_did) = impl_item_id.as_def_id() else { continue };
+ let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
+ let reject_cx =
+ DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
+ if !reject_cx.types_may_unify(aliased_ty, for_ty) {
+ continue;
+ }
+ // Avoid duplicates
+ if !seen_impls.insert(*impl_item_id) {
+ continue;
+ }
+ // This impl was not found in the set of rejected impls
+ aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone()));
+ }
+ }
+ }
+ let mut type_impl_collector = TypeImplCollector {
+ aliased_types: IndexMap::default(),
+ visited_aliases: FxHashSet::default(),
+ cache,
+ cx,
+ };
+ DocVisitor::visit_crate(&mut type_impl_collector, &krate);
+ // Final serialized form of the alias impl
+ struct AliasSerializableImpl {
+ text: String,
+ trait_: Option<String>,
+ aliases: Vec<String>,
+ }
+ impl Serialize for AliasSerializableImpl {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.text)?;
+ if let Some(trait_) = &self.trait_ {
+ seq.serialize_element(trait_)?;
+ } else {
+ seq.serialize_element(&0)?;
+ }
+ for type_ in &self.aliases {
+ seq.serialize_element(type_)?;
+ }
+ seq.end()
+ }
+ }
+ let cx = type_impl_collector.cx;
+ let dst = cx.dst.join("type.impl");
+ let aliased_types = type_impl_collector.aliased_types;
+ for aliased_type in aliased_types.values() {
+ let impls = aliased_type
+ .impl_
+ .values()
+ .flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
+ let mut ret = Vec::new();
+ let trait_ = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| format!("{:#}", trait_.print(cx)));
+ // render_impl will filter out "impossible-to-call" methods
+ // to make that functionality work here, it needs to be called with
+ // each type alias, and if it gives a different result, split the impl
+ for &(type_alias_fqp, ref type_alias_item) in type_aliases {
+ let mut buf = Buffer::html();
+ cx.id_map = Default::default();
+ cx.deref_id_map = Default::default();
+ let target_did = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| trait_.def_id())
+ .or_else(|| impl_.inner_impl().for_.def_id(cache));
+ let provided_methods;
+ let assoc_link = if let Some(target_did) = target_did {
+ provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx());
+ AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods)
+ } else {
+ AssocItemLink::Anchor(None)
+ };
+ super::render_impl(
+ &mut buf,
+ cx,
+ *impl_,
+ &type_alias_item,
+ assoc_link,
+ RenderMode::Normal,
+ None,
+ &[],
+ ImplRenderingParameters {
+ show_def_docs: true,
+ show_default_items: true,
+ show_non_assoc_items: true,
+ toggle_open_by_default: true,
+ },
+ );
+ let text = buf.into_inner();
+ let type_alias_fqp = (*type_alias_fqp).iter().join("::");
+ if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
+ ret.last_mut()
+ .expect("already established that ret.last() is Some()")
+ .aliases
+ .push(type_alias_fqp);
+ } else {
+ ret.push(AliasSerializableImpl {
+ text,
+ trait_: trait_.clone(),
+ aliases: vec![type_alias_fqp],
+ })
+ }
+ }
+ ret
+ })
+ .collect::<Vec<_>>();
+ let impls = format!(
+ r#""{}":{}"#,
+ krate.name(cx.tcx()),
+ serde_json::to_string(&impls).expect("failed serde conversion"),
+ );
+
+ let mut mydst = dst.clone();
+ for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
+ mydst.push(part.to_string());
+ }
+ cx.shared.ensure_dir(&mydst)?;
+ let aliased_item_type = aliased_type.target_type;
+ mydst.push(&format!(
+ "{aliased_item_type}.{}.js",
+ aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
+ ));
+
+ let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
+ all_impls.push(impls);
+ // Sort the implementors by crate so the file will be generated
+ // identically even with rustdoc running in parallel.
+ all_impls.sort();
+
+ let mut v = String::from("(function() {var type_impls = {\n");
+ v.push_str(&all_impls.join(",\n"));
+ v.push_str("\n};");
+ v.push_str(
+ "if (window.register_type_impls) {\
+ window.register_type_impls(type_impls);\
+ } else {\
+ window.pending_type_impls = type_impls;\
+ }",
+ );
+ v.push_str("})()");
+ cx.shared.fs.write(mydst, v)?;
+ }
+
// Update the list of all implementors for traits
- let dst = cx.dst.join("implementors");
- let cache = cx.cache();
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
+ let dst = cx.dst.join("trait.impl");
for (&did, imps) in &cache.implementors {
// Private modules can leak through to this phase of rustdoc, which
// could contain implementations for otherwise private types. In some
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1d6eafe51..ce620c226 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -1,4 +1,5 @@
use crate::clean;
+use crate::clean::utils::has_doc_flag;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::format;
@@ -12,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::source_map::FileName;
+use rustc_span::{sym, FileName};
use std::cell::RefCell;
use std::ffi::OsStr;
@@ -223,7 +224,8 @@ impl SourceCollector<'_, '_> {
cur.push(&fname);
let title = format!("{} - source", src_fname.to_string_lossy());
- let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
+ let desc =
+ format!("Source of the Rust file `{}`.", filename.prefer_remapped_unconditionaly());
let page = layout::Page {
title: &title,
css_class: "src",
@@ -231,6 +233,7 @@ impl SourceCollector<'_, '_> {
static_root_path: shared.static_root_path.as_deref(),
description: &desc,
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let v = layout::render(
&shared.layout,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 47f9e6502..9efdcd601 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -461,19 +461,9 @@ img {
display: none !important;
}
-.sidebar .logo-container {
- margin-top: 10px;
- margin-bottom: 10px;
- text-align: center;
-}
-
-.version {
- overflow-wrap: break-word;
-}
-
.logo-container > img {
- height: 100px;
- width: 100px;
+ height: 48px;
+ width: 48px;
}
ul.block, .block li {
@@ -502,6 +492,7 @@ ul.block, .block li {
}
.sidebar-elems,
+.sidebar > .version,
.sidebar > h2 {
padding-left: 24px;
}
@@ -510,6 +501,8 @@ ul.block, .block li {
color: var(--sidebar-link-color);
}
.sidebar .current,
+.sidebar .current a,
+.sidebar-crate a.logo-container:hover + h2 a,
.sidebar a:hover:not(.logo-container) {
background-color: var(--sidebar-current-link-background-color);
}
@@ -524,6 +517,75 @@ ul.block, .block li {
overflow: hidden;
}
+.sidebar-crate {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* there's a 10px padding at the top of <main>, and a 4px margin at the
+ top of the search form. To line them up, add them. */
+ margin: 14px 32px 1rem;
+ row-gap: 10px;
+ column-gap: 32px;
+ flex-wrap: wrap;
+}
+
+.sidebar-crate h2 {
+ flex-grow: 1;
+ /* This setup with the margins and row-gap is designed to make flex-wrap
+ work the way we want. If they're in the side-by-side lockup, there
+ should be a 16px margin to the left of the logo (visually the same as
+ the 24px one on everything else, which are not giant circles) and 8px
+ between it and the crate's name and version. When they're line wrapped,
+ the logo needs to have the same margin on both sides of itself (to
+ center properly) and the crate name and version need 24px on their
+ left margin. */
+ margin: 0 -8px;
+ /* To align this with the search bar, it should not be centered, even when
+ the logo is. */
+ align-self: start;
+}
+
+.sidebar-crate .logo-container {
+ /* The logo is expected to have 8px "slop" along its edges, so we can optically
+ center it. */
+ margin: 0 -16px 0 -16px;
+ text-align: center;
+}
+
+.sidebar-crate h2 a {
+ display: block;
+ margin: 0 calc(-24px + 0.25rem) 0 -0.5rem;
+ /* Align the sidebar crate link with the search bar, which have different
+ font sizes.
+
+ | | font-size | line-height | total line-height | padding-y | total |
+ |:-------|----------:|------------:|------------------:|----------:|-------------:|
+ | crate | 1.375rem | 1.25 | 1.72rem | x | 2x+1.72rem |
+ | search | 1rem | 1.15 | 1.15rem | 8px | 1.15rem+16px |
+
+ 2x + 1.72rem = 1.15rem + 16px
+ 2x = 1.15rem + 16px - 1.72rem
+ 2x = 16px - 0.57rem
+ x = ( 16px - 0.57rem ) / 2
+ */
+ padding: calc( ( 16px - 0.57rem ) / 2 ) 0.25rem;
+ padding-left: 0.5rem;
+}
+
+.sidebar-crate h2 .version {
+ display: block;
+ font-weight: normal;
+ font-size: 1rem;
+ overflow-wrap: break-word;
+ /* opposite of the link padding, cut in half again */
+ margin-top: calc( ( -16px + 0.57rem ) / 2 );
+}
+
+.sidebar-crate + .version {
+ margin-top: -1rem;
+ margin-bottom: 1rem;
+}
+
.mobile-topbar {
display: none;
}
@@ -1045,6 +1107,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
white-space: pre-wrap;
border-radius: 3px;
display: inline;
+ vertical-align: baseline;
}
.stab.portability > code {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index eb256455b..7c052606a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -51,9 +51,14 @@ function setMobileTopbar() {
// but with the current code it's hard to get the right information in the right place.
const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileTopbar && locationTitle) {
+ if (mobileTopbar) {
const mobileTitle = document.createElement("h2");
- mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTitle.className = "location";
+ if (hasClass(document.body, "crate")) {
+ mobileTitle.innerText = `Crate ${window.currentCrate}`;
+ } else if (locationTitle) {
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ }
mobileTopbar.appendChild(mobileTitle);
}
}
@@ -354,6 +359,34 @@ function preLoadCss(cssUrl) {
expandSection(pageId);
}
}
+ if (savedHash.startsWith("impl-")) {
+ // impl-disambiguated links, used by the search engine
+ // format: impl-X[-for-Y]/method.WHATEVER
+ // turn this into method.WHATEVER[-NUMBER]
+ const splitAt = savedHash.indexOf("/");
+ if (splitAt !== -1) {
+ const implId = savedHash.slice(0, splitAt);
+ const assocId = savedHash.slice(splitAt + 1);
+ const implElem = document.getElementById(implId);
+ if (implElem && implElem.parentElement.tagName === "SUMMARY" &&
+ implElem.parentElement.parentElement.tagName === "DETAILS") {
+ onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
+ `[id^="${assocId}"]`),
+ item => {
+ const numbered = /([^-]+)-([0-9]+)/.exec(item.id);
+ if (item.id === assocId || (numbered && numbered[1] === assocId)) {
+ openParentDetails(item);
+ item.scrollIntoView();
+ // Let the section expand itself before trying to highlight
+ setTimeout(() => {
+ window.location.replace("#" + item.id);
+ }, 0);
+ }
+ }
+ );
+ }
+ }
+ }
}
function onHashChange(ev) {
@@ -452,22 +485,27 @@ function preLoadCss(cssUrl) {
return;
}
+ const modpath = hasClass(document.body, "mod") ? "../" : "";
+
const h3 = document.createElement("h3");
- h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
+ h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;
const ul = document.createElement("ul");
ul.className = "block " + shortty;
for (const name of filtered) {
let path;
if (shortty === "mod") {
- path = name + "/index.html";
+ path = `${modpath}${name}/index.html`;
} else {
- path = shortty + "." + name + ".html";
+ path = `${modpath}${shortty}.${name}.html`;
+ }
+ let current_page = document.location.href.toString();
+ if (current_page.endsWith("/")) {
+ current_page += "index.html";
}
- const current_page = document.location.href.split("/").pop();
const link = document.createElement("a");
link.href = path;
- if (path === current_page) {
+ if (link.href === current_page) {
link.className = "current";
}
link.textContent = name;
@@ -480,23 +518,38 @@ function preLoadCss(cssUrl) {
}
if (sidebar) {
+ // keep this synchronized with ItemSection::ALL in html/render/mod.rs
+ // Re-exports aren't shown here, because they don't have child pages
+ //block("reexport", "reexports", "Re-exports");
block("primitive", "primitives", "Primitive Types");
block("mod", "modules", "Modules");
block("macro", "macros", "Macros");
block("struct", "structs", "Structs");
block("enum", "enums", "Enums");
- block("union", "unions", "Unions");
block("constant", "constants", "Constants");
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
block("type", "types", "Type Aliases");
+ block("union", "unions", "Unions");
+ // No point, because these items don't appear in modules
+ //block("impl", "impls", "Implementations");
+ //block("tymethod", "tymethods", "Type Methods");
+ //block("method", "methods", "Methods");
+ //block("structfield", "fields", "Fields");
+ //block("variant", "variants", "Variants");
+ //block("associatedtype", "associated-types", "Associated Types");
+ //block("associatedconstant", "associated-consts", "Associated Constants");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
+ block("opaque", "opaque-types", "Opaque Types");
+ block("attr", "attributes", "Attribute Macros");
+ block("derive", "derives", "Derive Macros");
block("traitalias", "trait-aliases", "Trait Aliases");
}
}
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
window.register_implementors = imp => {
const implementors = document.getElementById("implementors-list");
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
@@ -563,7 +616,7 @@ function preLoadCss(cssUrl) {
onEachLazy(code.getElementsByTagName("a"), elem => {
const href = elem.getAttribute("href");
- if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
elem.setAttribute("href", window.rootPath + href);
}
});
@@ -587,6 +640,216 @@ function preLoadCss(cssUrl) {
window.register_implementors(window.pending_implementors);
}
+ /**
+ * <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ *
+ * [RUSTDOCIMPL] type.impl
+ *
+ * This code inlines implementations into the type alias docs at runtime. It's done at
+ * runtime because some crates have many type aliases and many methods, and we don't want
+ * to generate *O*`(types*methods)` HTML text. The data inside is mostly HTML fragments,
+ * wrapped in JSON.
+ *
+ * - It only includes docs generated for the current crate. This function accepts an
+ * object mapping crate names to the set of impls.
+ *
+ * - It filters down to the set of applicable impls. The Rust type checker is used to
+ * tag each HTML blob with the set of type aliases that can actually use it, so the
+ * JS only needs to consult the attached list of type aliases.
+ *
+ * - It renames the ID attributes, to avoid conflicting IDs in the resulting DOM.
+ *
+ * - It adds the necessary items to the sidebar. If it's an inherent impl, that means
+ * adding methods, associated types, and associated constants. If it's a trait impl,
+ * that means adding it to the trait impl sidebar list.
+ *
+ * - It adds the HTML block itself. If it's an inherent impl, it goes after the type
+ * alias's own inherent impls. If it's a trait impl, it goes in the Trait
+ * Implementations section.
+ *
+ * - After processing all of the impls, it sorts the sidebar items by name.
+ *
+ * @param {{[cratename: string]: Array<Array<string|0>>}} impl
+ */
+ window.register_type_impls = imp => {
+ if (!imp || !imp[window.currentCrate]) {
+ return;
+ }
+ window.pending_type_impls = null;
+ const idMap = new Map();
+
+ let implementations = document.getElementById("implementations-list");
+ let trait_implementations = document.getElementById("trait-implementations-list");
+ let trait_implementations_header = document.getElementById("trait-implementations");
+
+ // We want to include the current type alias's impls, and no others.
+ const script = document.querySelector("script[data-self-path]");
+ const selfPath = script ? script.getAttribute("data-self-path") : null;
+
+ // These sidebar blocks need filled in, too.
+ const mainContent = document.querySelector("#main-content");
+ const sidebarSection = document.querySelector(".sidebar section");
+ let methods = document.querySelector(".sidebar .block.method");
+ let associatedTypes = document.querySelector(".sidebar .block.associatedtype");
+ let associatedConstants = document.querySelector(".sidebar .block.associatedconstant");
+ let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation");
+
+ for (const impList of imp[window.currentCrate]) {
+ const types = impList.slice(2);
+ const text = impList[0];
+ const isTrait = impList[1] !== 0;
+ const traitName = impList[1];
+ if (types.indexOf(selfPath) === -1) {
+ continue;
+ }
+ let outputList = isTrait ? trait_implementations : implementations;
+ if (outputList === null) {
+ const outputListName = isTrait ? "Trait Implementations" : "Implementations";
+ const outputListId = isTrait ?
+ "trait-implementations-list" :
+ "implementations-list";
+ const outputListHeaderId = isTrait ? "trait-implementations" : "implementations";
+ const outputListHeader = document.createElement("h2");
+ outputListHeader.id = outputListHeaderId;
+ outputListHeader.innerText = outputListName;
+ outputList = document.createElement("div");
+ outputList.id = outputListId;
+ if (isTrait) {
+ const link = document.createElement("a");
+ link.href = `#${outputListHeaderId}`;
+ link.innerText = "Trait Implementations";
+ const h = document.createElement("h3");
+ h.appendChild(link);
+ trait_implementations = outputList;
+ trait_implementations_header = outputListHeader;
+ sidebarSection.appendChild(h);
+ sidebarTraitList = document.createElement("ul");
+ sidebarTraitList.className = "block trait-implementation";
+ sidebarSection.appendChild(sidebarTraitList);
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ } else {
+ implementations = outputList;
+ if (trait_implementations) {
+ mainContent.insertBefore(outputListHeader, trait_implementations_header);
+ mainContent.insertBefore(outputList, trait_implementations_header);
+ } else {
+ const mainContent = document.querySelector("#main-content");
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ }
+ }
+ }
+ const template = document.createElement("template");
+ template.innerHTML = text;
+
+ onEachLazy(template.content.querySelectorAll("a"), elem => {
+ const href = elem.getAttribute("href");
+
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ elem.setAttribute("href", window.rootPath + href);
+ }
+ });
+ onEachLazy(template.content.querySelectorAll("[id]"), el => {
+ let i = 0;
+ if (idMap.has(el.id)) {
+ i = idMap.get(el.id);
+ } else if (document.getElementById(el.id)) {
+ i = 1;
+ while (document.getElementById(`${el.id}-${2 * i}`)) {
+ i = 2 * i;
+ }
+ while (document.getElementById(`${el.id}-${i}`)) {
+ i += 1;
+ }
+ }
+ if (i !== 0) {
+ const oldHref = `#${el.id}`;
+ const newHref = `#${el.id}-${i}`;
+ el.id = `${el.id}-${i}`;
+ onEachLazy(template.content.querySelectorAll("a[href]"), link => {
+ if (link.getAttribute("href") === oldHref) {
+ link.href = newHref;
+ }
+ });
+ }
+ idMap.set(el.id, i + 1);
+ });
+ const templateAssocItems = template.content.querySelectorAll("section.tymethod, " +
+ "section.method, section.associatedtype, section.associatedconstant");
+ if (isTrait) {
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.href = `#${template.content.querySelector(".impl").id}`;
+ a.textContent = traitName;
+ li.appendChild(a);
+ sidebarTraitList.append(li);
+ } else {
+ onEachLazy(templateAssocItems, item => {
+ let block = hasClass(item, "associatedtype") ? associatedTypes : (
+ hasClass(item, "associatedconstant") ? associatedConstants : (
+ methods));
+ if (!block) {
+ const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : (
+ hasClass(item, "associatedconstant") ? "Associated Constants" : (
+ "Methods"));
+ const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : (
+ hasClass(item, "associatedconstant") ? "associatedconstant" : (
+ "method"));
+ const blockHeader = document.createElement("h3");
+ const blockLink = document.createElement("a");
+ blockLink.href = "#implementations";
+ blockLink.innerText = blockTitle;
+ blockHeader.appendChild(blockLink);
+ block = document.createElement("ul");
+ block.className = `block ${blockClass}`;
+ const insertionReference = methods || sidebarTraitList;
+ if (insertionReference) {
+ const insertionReferenceH = insertionReference.previousElementSibling;
+ sidebarSection.insertBefore(blockHeader, insertionReferenceH);
+ sidebarSection.insertBefore(block, insertionReferenceH);
+ } else {
+ sidebarSection.appendChild(blockHeader);
+ sidebarSection.appendChild(block);
+ }
+ if (hasClass(item, "associatedtype")) {
+ associatedTypes = block;
+ } else if (hasClass(item, "associatedconstant")) {
+ associatedConstants = block;
+ } else {
+ methods = block;
+ }
+ }
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.innerText = item.id.split("-")[0].split(".")[1];
+ a.href = `#${item.id}`;
+ li.appendChild(a);
+ block.appendChild(li);
+ });
+ }
+ outputList.appendChild(template.content);
+ }
+
+ for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) {
+ if (!list) {
+ continue;
+ }
+ const newChildren = Array.prototype.slice.call(list.children);
+ newChildren.sort((a, b) => {
+ const aI = a.innerText;
+ const bI = b.innerText;
+ return aI < bI ? -1 :
+ aI > bI ? 1 :
+ 0;
+ });
+ list.replaceChildren(...newChildren);
+ }
+ };
+ if (window.pending_type_impls) {
+ window.register_type_impls(window.pending_type_impls);
+ }
+
function addSidebarCrates() {
if (!window.ALL_CRATES) {
return;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 2f0cae0a4..48c9a53a2 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1555,7 +1555,7 @@ function initSearch(rawSearchIndex) {
return false;
}
}
- } else if (fnType.id !== null) {
+ } else {
if (queryElem.id === typeNameIdOfArrayOrSlice &&
(fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
) {
@@ -1752,6 +1752,7 @@ function initSearch(rawSearchIndex) {
type: item.type,
is_alias: true,
deprecated: item.deprecated,
+ implDisambiguator: item.implDisambiguator,
};
}
@@ -2218,7 +2219,7 @@ function initSearch(rawSearchIndex) {
href = ROOT_PATH + name + "/index.html";
} else if (item.parent !== undefined) {
const myparent = item.parent;
- let anchor = "#" + type + "." + name;
+ let anchor = type + "." + name;
const parentType = itemTypes[myparent.ty];
let pageType = parentType;
let pageName = myparent.name;
@@ -2232,16 +2233,19 @@ function initSearch(rawSearchIndex) {
const enumName = item.path.substr(enumNameIdx + 2);
path = item.path.substr(0, enumNameIdx);
displayPath = path + "::" + enumName + "::" + myparent.name + "::";
- anchor = "#variant." + myparent.name + ".field." + name;
+ anchor = "variant." + myparent.name + ".field." + name;
pageType = "enum";
pageName = enumName;
} else {
displayPath = path + "::" + myparent.name + "::";
}
+ if (item.implDisambiguator !== null) {
+ anchor = item.implDisambiguator + "/" + anchor;
+ }
href = ROOT_PATH + path.replace(/::/g, "/") +
"/" + pageType +
"." + pageName +
- ".html" + anchor;
+ ".html#" + anchor;
} else {
displayPath = item.path + "::";
href = ROOT_PATH + item.path.replace(/::/g, "/") +
@@ -2727,6 +2731,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* Types are also represented as arrays; the first item is an index into the `p`
* array, while the second is a list of types representing any generic parameters.
*
+ * b[i] contains an item's impl disambiguator. This is only present if an item
+ * is defined in an impl block and, the impl block's type has more than one associated
+ * item with the same name.
+ *
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
* points into the n/t/d/q/i/f arrays.
*
@@ -2746,6 +2754,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* i: Array<Number>,
* f: Array<RawFunctionSearchType>,
* p: Array<Object>,
+ * b: Array<[Number, String]>,
* c: Array<Number>
* }}
*/
@@ -2766,6 +2775,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
deprecated: null,
+ implDisambiguator: null,
};
id += 1;
searchIndex.push(crateRow);
@@ -2789,6 +2799,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
const itemFunctionSearchTypes = crateCorpus.f;
// an array of (Number) indices for the deprecated items
const deprecatedItems = new Set(crateCorpus.c);
+ // an array of (Number) indices for the deprecated items
+ const implDisambiguator = new Map(crateCorpus.b);
// an array of [(Number) item type,
// (String) name]
const paths = crateCorpus.p;
@@ -2849,6 +2861,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
deprecated: deprecatedItems.has(i),
+ implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
};
id += 1;
searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 579c782be..3f6147bb9 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -10,7 +10,6 @@
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {# #}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_bold}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {# #}
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
@@ -42,6 +41,8 @@
<script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #}
{% else if !page.css_class.contains("mod") %}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #}
+ {% else if !page.css_class.contains("sys") %}
+ <script defer src="../sidebar-items{{page.resource_suffix}}.js"></script> {# #}
{% endif %}
<script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {# #}
{% if layout.scrape_examples_extension %}
@@ -77,36 +78,51 @@
{% if page.css_class != "src" %}
<nav class="mobile-topbar"> {# #}
<button class="sidebar-menu-toggle">&#9776;</button> {# #}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt=""> {# #}
{% endif %}
</a> {# #}
+ {% endif %}
</nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ <div class="sidebar-crate">
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="logo"> {# #}
+ {% endif %}
+ </a> {# #}
{% endif %}
- </a> {# #}
+ <h2> {# #}
+ <a href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">{{display_krate}}</a> {# #}
+ {% if !display_krate_version_number.is_empty() %}
+ <span class="version">{{+ display_krate_version_number}}</span>
+ {% endif %}
+ </h2> {# #}
+ </div> {# #}
+ {% if !display_krate_version_extra.is_empty() %}
+ <div class="version">{{+ display_krate_version_extra}}</div> {# #}
+ {% endif %}
{% endif %}
{{ sidebar|safe }}
</nav> {# #}
<main> {# #}
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
<nav class="sub"> {# #}
- {% if page.css_class == "src" %}
- <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if page.css_class == "src" && (!layout.logo.is_empty() || page.rust_logo) %}
+ <a class="sub-logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="{{display_krate}}"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="{{display_krate}}"> {# #}
{% endif %}
</a> {# #}
{% endif %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
index 01d476ad2..d98213418 100644
--- a/src/librustdoc/html/templates/sidebar.html
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -6,9 +6,6 @@
<div class="sidebar-elems">
{% if is_crate %}
<ul class="block">
- {% if !version.is_empty() %}
- <li class="version">Version {{+ version}}</li>
- {% endif %}
<li><a id="all-types" href="all.html">All Items</a></li> {# #}
</ul>
{% endif %}
@@ -21,7 +18,7 @@
<h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
{% endif %}
{% if !block.links.is_empty() %}
- <ul class="block">
+ <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}">
{% for link in block.links %}
<li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
{% endfor %}
@@ -32,6 +29,6 @@
</section>
{% endif %}
{% if !path.is_empty() %}
- <h2><a href="index.html">In {{+ path}}</a></h2>
+ <h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path}}</a></h2>
{% endif %}
</div>