summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r--src/librustdoc/html/render/context.rs74
-rw-r--r--src/librustdoc/html/render/mod.rs77
-rw-r--r--src/librustdoc/html/render/print_item.rs449
-rw-r--r--src/librustdoc/html/render/search_index.rs491
-rw-r--r--src/librustdoc/html/render/sidebar.rs46
5 files changed, 717 insertions, 420 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index d7ff248a9..97714afaa 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,8 +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};
@@ -22,13 +24,13 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
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::FormatRenderer;
+use crate::formats::{self, 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};
@@ -105,8 +107,8 @@ pub(crate) struct SharedContext<'tcx> {
pub(super) module_sorting: ModuleSorting,
/// Additional CSS files to be added to the generated docs.
pub(crate) style_files: Vec<StylePath>,
- /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
- /// "light-v2.css").
+ /// Suffix to add on resource files (if suffix is "-v2" then "search-index.js" becomes
+ /// "search-index-v2.js").
pub(crate) resource_suffix: String,
/// Optional path string to be used to load static files on output pages. If not set, uses
/// combinations of `../` to reach the documentation root.
@@ -147,6 +149,53 @@ 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> {
@@ -463,6 +512,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
generate_link_to_definition,
call_locations,
no_emit_shared,
+ html_no_source,
..
} = options;
@@ -488,7 +538,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
scrape_examples_extension: !call_locations.is_empty(),
};
let mut issue_tracker_base_url = None;
- let mut include_sources = true;
+ let mut include_sources = !html_no_source;
// Crawl the crate attributes looking for attributes which control how we're
// going to emit HTML
@@ -664,21 +714,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
You need to enable JavaScript be able to update your settings.\
</section>\
</noscript>\
- <link rel=\"stylesheet\" \
- href=\"{static_root_path}{settings_css}\">\
- <script defer src=\"{static_root_path}{settings_js}\"></script>\
- <link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
- as=\"style\">",
+ <script defer src=\"{static_root_path}{settings_js}\"></script>",
static_root_path = page.get_static_root_path(),
- settings_css = static_files::STATIC_FILES.settings_css,
settings_js = static_files::STATIC_FILES.settings_js,
- theme_light_css = static_files::STATIC_FILES.theme_light_css,
- theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
- theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
);
// Pre-load all theme CSS files, so that switching feels seamless.
//
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index ac9c180a6..3e671a64b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
- pub(crate) parent_idx: Option<usize>,
+ pub(crate) parent_idx: Option<isize>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@ impl Serialize for RenderType {
let id = match &self.id {
// 0 is a sentinel, everything else is one-indexed
None => 0,
- Some(RenderTypeId::Index(idx)) => idx + 1,
+ // concrete type
+ Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+ // generic type parameter
+ Some(RenderTypeId::Index(idx)) => *idx,
_ => panic!("must convert render types to indexes before serializing"),
};
if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@ impl Serialize for RenderType {
pub(crate) enum RenderTypeId {
DefId(DefId),
Primitive(clean::PrimitiveType),
- Index(usize),
+ Index(isize),
}
/// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
pub(crate) struct IndexItemFunctionType {
inputs: Vec<RenderType>,
output: Vec<RenderType>,
+ where_clause: Vec<Vec<RenderType>>,
}
impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
_ => seq.serialize_element(&self.inputs)?,
}
match &self.output[..] {
- [] => {}
+ [] if self.where_clause.is_empty() => {}
[one] if one.generics.is_none() => seq.serialize_element(one)?,
_ => seq.serialize_element(&self.output)?,
}
+ for constraint in &self.where_clause {
+ if let [one] = &constraint[..] && one.generics.is_none() {
+ seq.serialize_element(one)?;
+ } else {
+ seq.serialize_element(constraint)?;
+ }
+ }
seq.end()
}
}
@@ -235,7 +246,7 @@ struct AllTypes {
traits: FxHashSet<ItemEntry>,
macros: FxHashSet<ItemEntry>,
functions: FxHashSet<ItemEntry>,
- typedefs: FxHashSet<ItemEntry>,
+ type_aliases: FxHashSet<ItemEntry>,
opaque_tys: FxHashSet<ItemEntry>,
statics: FxHashSet<ItemEntry>,
constants: FxHashSet<ItemEntry>,
@@ -255,7 +266,7 @@ impl AllTypes {
traits: new_set(100),
macros: new_set(100),
functions: new_set(100),
- typedefs: new_set(100),
+ type_aliases: new_set(100),
opaque_tys: new_set(100),
statics: new_set(100),
constants: new_set(100),
@@ -279,7 +290,7 @@ impl AllTypes {
ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
- ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
+ ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)),
ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
@@ -317,8 +328,8 @@ impl AllTypes {
if !self.functions.is_empty() {
sections.insert(ItemSection::Functions);
}
- if !self.typedefs.is_empty() {
- sections.insert(ItemSection::TypeDefinitions);
+ if !self.type_aliases.is_empty() {
+ sections.insert(ItemSection::TypeAliases);
}
if !self.opaque_tys.is_empty() {
sections.insert(ItemSection::OpaqueTypes);
@@ -374,7 +385,7 @@ impl AllTypes {
print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
print_entries(f, &self.functions, ItemSection::Functions);
- print_entries(f, &self.typedefs, ItemSection::TypeDefinitions);
+ print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes);
print_entries(f, &self.statics, ItemSection::Statics);
@@ -403,7 +414,8 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
- heading_offset: HeadingOffset::H1
+ heading_offset: HeadingOffset::H1,
+ custom_code_classes_in_docs: false,
}
.into_string()
)
@@ -437,6 +449,7 @@ fn render_markdown<'a, 'cx: 'a>(
heading_offset: HeadingOffset,
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(move |f| {
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
f,
"<div class=\"docblock\">{}</div>",
@@ -448,6 +461,7 @@ fn render_markdown<'a, 'cx: 'a>(
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset,
+ custom_code_classes_in_docs,
}
.into_string()
)
@@ -1117,13 +1131,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(it);
+ derefs.insert(did);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
Ok(())
})
}
@@ -1132,15 +1146,17 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- 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());
+ 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();
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
@@ -1169,6 +1185,9 @@ 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,
@@ -1214,8 +1233,10 @@ 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().partition(|t| t.inner_impl().kind.is_blanket());
+ 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());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1237,7 +1258,7 @@ fn render_deref_methods(
.iter()
.find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -1771,6 +1792,7 @@ fn render_impl(
</div>",
);
}
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
w,
"<div class=\"docblock\">{}</div>",
@@ -1781,7 +1803,8 @@ fn render_impl(
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
- heading_offset: HeadingOffset::H4
+ heading_offset: HeadingOffset::H4,
+ custom_code_classes_in_docs,
}
.into_string()
);
@@ -2035,7 +2058,7 @@ pub(crate) enum ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2067,7 +2090,7 @@ impl ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2093,7 +2116,7 @@ impl ItemSection {
Self::Unions => "unions",
Self::Enums => "enums",
Self::Functions => "functions",
- Self::TypeDefinitions => "types",
+ Self::TypeAliases => "types",
Self::Statics => "statics",
Self::Constants => "constants",
Self::Traits => "traits",
@@ -2123,7 +2146,7 @@ impl ItemSection {
Self::Unions => "Unions",
Self::Enums => "Enums",
Self::Functions => "Functions",
- Self::TypeDefinitions => "Type Definitions",
+ Self::TypeAliases => "Type Aliases",
Self::Statics => "Statics",
Self::Constants => "Constants",
Self::Traits => "Traits",
@@ -2154,7 +2177,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
ItemType::Union => ItemSection::Unions,
ItemType::Enum => ItemSection::Enums,
ItemType::Function => ItemSection::Functions,
- ItemType::Typedef => ItemSection::TypeDefinitions,
+ ItemType::TypeAlias => ItemSection::TypeAliases,
ItemType::Static => ItemSection::Statics,
ItemType::Constant => ItemSection::Constants,
ItemType::Trait => ItemSection::Traits,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 6cab34986..c6751c958 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -198,7 +198,7 @@ pub(super) fn print_item(
clean::StructItem(..) => "Struct ",
clean::UnionItem(..) => "Union ",
clean::EnumItem(..) => "Enum ",
- clean::TypedefItem(..) => "Type Definition ",
+ clean::TypeAliasItem(..) => "Type Alias ",
clean::MacroItem(..) => "Macro ",
clean::ProcMacroItem(ref mac) => match mac.kind {
MacroKind::Bang => "Macro ",
@@ -273,7 +273,7 @@ pub(super) fn print_item(
clean::StructItem(ref s) => item_struct(buf, cx, item, s),
clean::UnionItem(ref s) => item_union(buf, cx, item, s),
clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
- clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t),
+ clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t),
clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
@@ -343,7 +343,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
ItemType::Static => 8,
ItemType::Trait => 9,
ItemType::Function => 10,
- ItemType::Typedef => 12,
+ ItemType::TypeAlias => 12,
ItemType::Union => 13,
_ => 14 + ty as u8,
}
@@ -1217,8 +1217,8 @@ fn item_opaque_ty(
.unwrap();
}
-fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
+ fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
wrap_item(w, |w| {
write!(
w,
@@ -1237,6 +1237,75 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if let Some(inner_type) = &t.inner_type {
+ write!(
+ w,
+ "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+ Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
+ );
+
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+ let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ wrap_item(w, |w| {
+ let variants_len = variants.len();
+ let variants_count = variants_iter().count();
+ let has_stripped_entries = variants_len != variants_count;
+
+ write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_enum_fields(
+ w,
+ cx,
+ Some(&t.generics),
+ variants_iter(),
+ variants_count,
+ has_stripped_entries,
+ *is_non_exhaustive,
+ )
+ });
+ item_variants(w, cx, it, variants_iter());
+ }
+ clean::TypeAliasInnerType::Union { fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ None,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ *ctor_kind,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ }
+ }
+
let def_id = it.item_id.expect_def_id();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
@@ -1315,6 +1384,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
s: &'a [clean::Item],
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(|f| {
+ if s.iter()
+ .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+ {
+ return f.write_str("/* private fields */");
+ }
+
for (i, ty) in s.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
@@ -1332,7 +1407,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
let tcx = cx.tcx();
let count_variants = e.variants().count();
- wrap_item(w, |mut w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it, tcx);
write!(
w,
@@ -1341,148 +1416,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
- if !print_where_clause_and_check(w, &e.generics, cx) {
- // If there wasn't a `where` clause, we add a whitespace.
- w.write_str(" ");
- }
+ render_enum_fields(
+ w,
+ cx,
+ Some(&e.generics),
+ e.variants(),
+ count_variants,
+ e.has_stripped_entries(),
+ it.is_non_exhaustive(),
+ );
+ });
- let variants_stripped = e.has_stripped_entries();
- if count_variants == 0 && !variants_stripped {
- w.write_str("{}");
- } else {
- w.write_str("{\n");
- let toggle = should_hide_fields(count_variants);
- if toggle {
- toggle_open(&mut w, format_args!("{count_variants} variants"));
- }
- for v in e.variants() {
- w.write_str(" ");
- 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::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
- }
- clean::VariantKind::Struct(ref s) => {
- render_struct(w, v, None, None, &s.fields, " ", false, cx);
- }
- },
- _ => unreachable!(),
- }
- w.write_str(",\n");
- }
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- if variants_stripped && !it.is_non_exhaustive() {
- w.write_str(" // some variants omitted\n");
- }
- if toggle {
- toggle_close(&mut w);
+ if count_variants != 0 {
+ item_variants(w, cx, it, e.variants());
+ }
+ 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>(
+ mut w: &mut Buffer,
+ cx: &mut Context<'_>,
+ g: Option<&clean::Generics>,
+ variants: impl Iterator<Item = &'a clean::Item>,
+ count_variants: usize,
+ has_stripped_entries: bool,
+ is_non_exhaustive: bool,
+) {
+ 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(" ");
+ }
+
+ let variants_stripped = has_stripped_entries;
+ if count_variants == 0 && !variants_stripped {
+ w.write_str("{}");
+ } else {
+ w.write_str("{\n");
+ let toggle = should_hide_fields(count_variants);
+ if toggle {
+ toggle_open(&mut w, format_args!("{count_variants} variants"));
+ }
+ const TAB: &str = " ";
+ for v in variants {
+ 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::Tuple(ref s) => {
+ write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ }
+ clean::VariantKind::Struct(ref s) => {
+ render_struct(w, v, None, None, &s.fields, TAB, false, cx);
+ }
+ },
+ _ => unreachable!(),
}
- w.write_str("}");
+ w.write_str(",\n");
}
- });
- write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if variants_stripped && !is_non_exhaustive {
+ w.write_str(" // some variants omitted\n");
+ }
+ if toggle {
+ toggle_close(&mut w);
+ }
+ w.write_str("}");
+ }
+}
- if count_variants != 0 {
+fn item_variants<'a>(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ variants: impl Iterator<Item = &'a clean::Item>,
+) {
+ let tcx = cx.tcx();
+ write!(
+ w,
+ "<h2 id=\"variants\" class=\"variants small-section-header\">\
+ Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
+ </h2>\
+ {}\
+ <div class=\"variants\">",
+ document_non_exhaustive_header(it),
+ document_non_exhaustive(it)
+ );
+ for variant in variants {
+ let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
- "<h2 id=\"variants\" class=\"variants small-section-header\">\
- Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
- </h2>\
- {}\
- <div class=\"variants\">",
- document_non_exhaustive_header(it),
- document_non_exhaustive(it)
+ "<section id=\"{id}\" class=\"variant\">\
+ <a href=\"#{id}\" class=\"anchor\">§</a>",
);
- for variant in e.variants() {
- let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<section id=\"{id}\" class=\"variant\">\
- <a href=\"#{id}\" class=\"anchor\">§</a>",
- );
- render_stability_since_raw_with_extra(
- w,
- variant.stable_since(tcx),
- variant.const_stability(tcx),
- it.stable_since(tcx),
- it.const_stable_since(tcx),
- " rightside",
- );
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ render_stability_since_raw_with_extra(
+ w,
+ variant.stable_since(tcx),
+ variant.const_stability(tcx),
+ it.stable_since(tcx),
+ it.const_stable_since(tcx),
+ " rightside",
+ );
+ write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
- let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
+ let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
- if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
- write!(w, "({})", print_tuple_struct_fields(cx, s));
- }
- w.write_str("</h3></section>");
-
- let heading_and_fields = match &variant_data.kind {
- clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
- clean::VariantKind::Tuple(fields) => {
- // Documentation on tuple variant fields is rare, so to reduce noise we only emit
- // the section if at least one field is documented.
- if fields.iter().any(|f| !f.doc_value().is_empty()) {
- Some(("Tuple Fields", fields))
- } else {
- None
- }
+ if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
+ write!(w, "({})", print_tuple_struct_fields(cx, s));
+ }
+ w.write_str("</h3></section>");
+
+ let heading_and_fields = match &variant_data.kind {
+ clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+ clean::VariantKind::Tuple(fields) => {
+ // Documentation on tuple variant fields is rare, so to reduce noise we only emit
+ // the section if at least one field is documented.
+ if fields.iter().any(|f| !f.doc_value().is_empty()) {
+ Some(("Tuple Fields", fields))
+ } else {
+ None
}
- clean::VariantKind::CLike => None,
- };
+ }
+ clean::VariantKind::CLike => None,
+ };
- if let Some((heading, fields)) = heading_and_fields {
- let variant_id =
- cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<div class=\"sub-variant\" id=\"{variant_id}\">\
- <h4>{heading}</h4>\
- {}",
- document_non_exhaustive(variant)
- );
- for field in fields {
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(_)) => {}
- clean::StructFieldItem(ref ty) => {
- let id = cx.derive_id(format!(
- "variant.{}.field.{}",
- variant.name.unwrap(),
- field.name.unwrap()
- ));
- write!(
- w,
- "<div class=\"sub-variant-field\">\
+ if let Some((heading, fields)) = heading_and_fields {
+ let variant_id =
+ cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
+ write!(
+ w,
+ "<div class=\"sub-variant\" id=\"{variant_id}\">\
+ <h4>{heading}</h4>\
+ {}",
+ document_non_exhaustive(variant)
+ );
+ for field in fields {
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(_)) => {}
+ clean::StructFieldItem(ref ty) => {
+ let id = cx.derive_id(format!(
+ "variant.{}.field.{}",
+ variant.name.unwrap(),
+ field.name.unwrap()
+ ));
+ write!(
+ w,
+ "<div class=\"sub-variant-field\">\
<span id=\"{id}\" class=\"small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}: {t}</code>\
</span>",
- f = field.name.unwrap(),
- t = ty.print(cx),
- );
- write!(
- w,
- "{}</div>",
- document(cx, field, Some(variant), HeadingOffset::H5)
- );
- }
- _ => unreachable!(),
+ f = field.name.unwrap(),
+ t = ty.print(cx),
+ );
+ write!(
+ w,
+ "{}</div>",
+ document(cx, field, Some(variant), HeadingOffset::H5)
+ );
}
+ _ => unreachable!(),
}
- w.write_str("</div>");
}
-
- write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+ w.write_str("</div>");
}
- write!(w, "</div>");
+
+ write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
}
- 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));
+ write!(w, "</div>");
}
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1593,15 +1699,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- let mut fields = s
- .fields
+ item_fields(w, cx, it, &s.fields, s.ctor_kind);
+
+ 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 item_fields(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ fields: &Vec<clean::Item>,
+ ctor_kind: Option<CtorKind>,
+) {
+ let mut fields = fields
.iter()
.filter_map(|f| match *f.kind {
clean::StructFieldItem(ref ty) => Some((f, ty)),
_ => None,
})
.peekable();
- if let None | Some(CtorKind::Fn) = s.ctor_kind {
+ if let None | Some(CtorKind::Fn) = ctor_kind {
if fields.peek().is_some() {
write!(
w,
@@ -1609,7 +1728,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>\
{}",
- if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+ if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
@@ -1630,9 +1749,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
}
}
- 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 item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1871,7 +1987,7 @@ fn render_union<'a, 'cx: 'a>(
}
fn render_struct(
- mut w: &mut Buffer,
+ w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
ty: Option<CtorKind>,
@@ -1891,6 +2007,29 @@ fn render_struct(
if let Some(g) = g {
write!(w, "{}", g.print(cx))
}
+ render_struct_fields(
+ w,
+ g,
+ ty,
+ fields,
+ tab,
+ structhead,
+ it.has_stripped_entries().unwrap_or(false),
+ cx,
+ )
+}
+
+fn render_struct_fields(
+ mut w: &mut Buffer,
+ g: Option<&clean::Generics>,
+ ty: Option<CtorKind>,
+ fields: &[clean::Item],
+ tab: &str,
+ structhead: bool,
+ has_stripped_entries: bool,
+ cx: &Context<'_>,
+) {
+ let tcx = cx.tcx();
match ty {
None => {
let where_displayed =
@@ -1922,11 +2061,11 @@ fn render_struct(
}
if has_visible_fields {
- if it.has_stripped_entries().unwrap() {
+ if has_stripped_entries {
write!(w, "\n{tab} /* private fields */");
}
write!(w, "\n{tab}");
- } else if it.has_stripped_entries().unwrap() {
+ } else if has_stripped_entries {
write!(w, " /* private fields */ ");
}
if toggle {
@@ -1936,21 +2075,31 @@ fn render_struct(
}
Some(CtorKind::Fn) => {
w.write_str("(");
- for (i, field) in fields.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
- clean::StructFieldItem(ref ty) => {
- write!(
- w,
- "{}{}",
- visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
- ty.print(cx),
- )
+ if fields.iter().all(|field| {
+ matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+ }) {
+ write!(w, "/* private fields */");
+ } else {
+ for (i, field) in fields.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ");
+ }
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+ clean::StructFieldItem(ref ty) => {
+ write!(
+ w,
+ "{}{}",
+ visibility_print_with_space(
+ field.visibility(tcx),
+ field.item_id,
+ cx
+ ),
+ ty.print(cx),
+ )
+ }
+ _ => unreachable!(),
}
- _ => unreachable!(),
}
}
w.write_str(")");
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index f34be120d..78c443b22 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -1,10 +1,10 @@
use std::collections::hash_map::Entry;
use std::collections::BTreeMap;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
+use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
use crate::clean;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -68,19 +68,19 @@ pub(crate) fn build_index<'tcx>(
// Reduce `DefId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
let mut lastpath = "";
- let mut lastpathid = 0usize;
+ let mut lastpathid = 0isize;
// First, on function signatures
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
for item in search_index.iter_mut() {
fn insert_into_map<F: std::hash::Hash + Eq>(
ty: &mut RenderType,
- map: &mut FxHashMap<F, usize>,
+ map: &mut FxHashMap<F, isize>,
itemid: F,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
- path: Symbol,
+ path: &[Symbol],
) {
match map.entry(itemid) {
Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
@@ -88,7 +88,7 @@ pub(crate) fn build_index<'tcx>(
let pathid = *lastpathid;
entry.insert(pathid);
*lastpathid += 1;
- crate_paths.push((item_type, path));
+ crate_paths.push((item_type, path.to_vec()));
ty.id = Some(RenderTypeId::Index(pathid));
}
}
@@ -97,10 +97,10 @@ pub(crate) fn build_index<'tcx>(
fn convert_render_type(
ty: &mut RenderType,
cache: &mut Cache,
- itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
- primitives: &mut FxHashMap<Symbol, usize>,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+ primitives: &mut FxHashMap<Symbol, isize>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
) {
if let Some(generics) = &mut ty.generics {
for item in generics {
@@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
item_type,
- *fqp.last().unwrap(),
+ fqp,
);
} else {
ty.id = None;
@@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
ItemType::Primitive,
- sym,
+ &[sym],
);
}
RenderTypeId::Index(_) => {}
@@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
&mut crate_paths,
);
}
+ for constraint in &mut search_type.where_clause {
+ for trait_ in &mut constraint[..] {
+ convert_render_type(
+ trait_,
+ cache,
+ &mut itemid_to_pathid,
+ &mut primitives,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ }
}
}
@@ -191,7 +203,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid += 1;
if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, *fqp.last().unwrap()));
+ crate_paths.push((short, fqp.clone()));
Some(pathid)
} else {
None
@@ -213,118 +225,163 @@ pub(crate) fn build_index<'tcx>(
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
- paths: Vec<(ItemType, Symbol)>,
+ paths: Vec<(ItemType, Vec<Symbol>)>,
// The String is alias name and the vec is the list of the elements with this alias.
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
}
+ struct Paths {
+ ty: ItemType,
+ name: Symbol,
+ path: Option<usize>,
+ }
+
+ impl Serialize for Paths {
+ 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.ty)?;
+ seq.serialize_element(self.name.as_str())?;
+ if let Some(ref path) = self.path {
+ seq.serialize_element(path)?;
+ }
+ seq.end()
+ }
+ }
+
impl<'a> Serialize for CrateData<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
+ let mut extra_paths = FxHashMap::default();
+ // We need to keep the order of insertion, hence why we use an `IndexMap`. Then we will
+ // insert these "extra paths" (which are paths of items from external crates) into the
+ // `full_paths` list at the end.
+ let mut revert_extra_paths = FxIndexMap::default();
+ let mut mod_paths = FxHashMap::default();
+ for (index, item) in self.items.iter().enumerate() {
+ if item.path.is_empty() {
+ continue;
+ }
+ mod_paths.insert(&item.path, index);
+ }
+ let mut paths = Vec::with_capacity(self.paths.len());
+ for (ty, path) in &self.paths {
+ if path.len() < 2 {
+ paths.push(Paths { ty: *ty, name: path[0], path: None });
+ continue;
+ }
+ let full_path = join_with_double_colon(&path[..path.len() - 1]);
+ if let Some(index) = mod_paths.get(&full_path) {
+ paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
+ continue;
+ }
+ // It means it comes from an external crate so the item and its path will be
+ // stored into another array.
+ //
+ // `index` is put after the last `mod_paths`
+ let index = extra_paths.len() + self.items.len();
+ if !revert_extra_paths.contains_key(&index) {
+ revert_extra_paths.insert(index, full_path.clone());
+ }
+ match extra_paths.entry(full_path) {
+ Entry::Occupied(entry) => {
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(*entry.get()),
+ });
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(index);
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(index),
+ });
+ }
+ }
+ }
+
+ let mut names = Vec::with_capacity(self.items.len());
+ let mut types = String::with_capacity(self.items.len());
+ let mut full_paths = Vec::with_capacity(self.items.len());
+ let mut descriptions = Vec::with_capacity(self.items.len());
+ let mut parents = Vec::with_capacity(self.items.len());
+ let mut functions = Vec::with_capacity(self.items.len());
+ let mut deprecated = Vec::with_capacity(self.items.len());
+
+ for (index, item) in self.items.iter().enumerate() {
+ let n = item.ty as u8;
+ let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
+ assert!(c <= 'z', "item types must fit within ASCII printables");
+ types.push(c);
+
+ assert_eq!(
+ item.parent.is_some(),
+ item.parent_idx.is_some(),
+ "`{}` is missing idx",
+ item.name
+ );
+ // 0 is a sentinel, everything else is one-indexed
+ parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0));
+
+ names.push(item.name.as_str());
+ descriptions.push(&item.desc);
+
+ if !item.path.is_empty() {
+ full_paths.push((index, &item.path));
+ }
+
+ // Fake option to get `0` out as a sentinel instead of `null`.
+ // We want to use `0` because it's three less bytes.
+ enum FunctionOption<'a> {
+ Function(&'a IndexItemFunctionType),
+ None,
+ }
+ impl<'a> Serialize for FunctionOption<'a> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ match self {
+ FunctionOption::None => 0.serialize(serializer),
+ FunctionOption::Function(ty) => ty.serialize(serializer),
+ }
+ }
+ }
+ functions.push(match &item.search_type {
+ Some(ty) => FunctionOption::Function(ty),
+ None => FunctionOption::None,
+ });
+
+ if item.deprecation.is_some() {
+ deprecated.push(index);
+ }
+ }
+
+ for (index, path) in &revert_extra_paths {
+ full_paths.push((*index, path));
+ }
+
let has_aliases = !self.aliases.is_empty();
let mut crate_data =
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
crate_data.serialize_field("doc", &self.doc)?;
- crate_data.serialize_field(
- "t",
- &self
- .items
- .iter()
- .map(|item| {
- let n = item.ty as u8;
- let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
- assert!(c <= 'z', "item types must fit within ASCII printables");
- c
- })
- .collect::<String>(),
- )?;
- crate_data.serialize_field(
- "n",
- &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "q",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of item indices and full paths
- .filter_map(
- |(index, item)| {
- if item.path.is_empty() { None } else { Some((index, &item.path)) }
- },
- )
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "d",
- &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "i",
- &self
- .items
- .iter()
- .map(|item| {
- assert_eq!(
- item.parent.is_some(),
- item.parent_idx.is_some(),
- "`{}` is missing idx",
- item.name
- );
- // 0 is a sentinel, everything else is one-indexed
- item.parent_idx.map(|x| x + 1).unwrap_or(0)
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "f",
- &self
- .items
- .iter()
- .map(|item| {
- // Fake option to get `0` out as a sentinel instead of `null`.
- // We want to use `0` because it's three less bytes.
- enum FunctionOption<'a> {
- Function(&'a IndexItemFunctionType),
- None,
- }
- impl<'a> Serialize for FunctionOption<'a> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- match self {
- FunctionOption::None => 0.serialize(serializer),
- FunctionOption::Function(ty) => ty.serialize(serializer),
- }
- }
- }
- match &item.search_type {
- Some(ty) => FunctionOption::Function(ty),
- None => FunctionOption::None,
- }
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "c",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of deprecated item indices
- .filter_map(|(index, item)| item.deprecation.map(|_| index))
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "p",
- &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
- )?;
+ crate_data.serialize_field("t", &types)?;
+ crate_data.serialize_field("n", &names)?;
+ // Serialize as an array of item indices and full paths
+ crate_data.serialize_field("q", &full_paths)?;
+ crate_data.serialize_field("d", &descriptions)?;
+ crate_data.serialize_field("i", &parents)?;
+ crate_data.serialize_field("f", &functions)?;
+ crate_data.serialize_field("c", &deprecated)?;
+ crate_data.serialize_field("p", &paths)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -357,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
- let (mut inputs, mut output) = match *item.kind {
+ let (mut inputs, mut output, where_clause) = match *item.kind {
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -367,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
inputs.retain(|a| a.id.is_some() || a.generics.is_some());
output.retain(|a| a.id.is_some() || a.generics.is_some());
- Some(IndexItemFunctionType { inputs, output })
+ Some(IndexItemFunctionType { inputs, output, where_clause })
}
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -387,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_id(type_)
}
- // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+ // The type parameters are converted to generics in `simplify_fn_type`
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+ clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
// Not supported yet
clean::BareFunction(_)
| clean::Generic(_)
| clean::ImplTrait(_)
- | clean::Tuple(_)
| clean::QPath { .. }
| clean::Infer => None,
}
}
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+ // other kinds of type parameters are identified by their name
+ Symbol(Symbol),
+ // every argument-position impl trait is its own type parameter
+ Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
self_: Option<&'a Type>,
generics: &Generics,
arg: &'a Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
res: &mut Vec<RenderType>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+ is_return: bool,
cache: &Cache,
) {
- fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
- // generics and impl trait are both identified by their generics,
- // rather than a type name itself
- let anonymous = ty.is_full_generic() || ty.is_impl_trait();
- let generics_empty = generics.is_empty();
-
- if anonymous {
- if generics_empty {
- // This is a type parameter with no trait bounds (for example: `T` in
- // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
- // with an empty type with an empty name. Let's just discard it.
- return;
- } else if generics.len() == 1 {
- // In this case, no need to go through an intermediate state if the type parameter
- // contains only one trait bound.
- //
- // For example:
- //
- // `fn foo<T: Display>(r: Option<T>) {}`
- //
- // In this case, it would contain:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "",
- // generics: [
- // name: "Display",
- // generics: []
- // }]
- // }]
- // }]
- // ```
- //
- // After removing the intermediate (unnecessary) type parameter, it'll become:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "Display",
- // generics: []
- // }]
- // }]
- // ```
- //
- // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
- // need to keep it as is!
- res.push(generics.pop().unwrap());
- return;
- }
- }
- let index_ty = get_index_type(&ty, generics);
- if index_ty.id.is_none() && generics_empty {
- return;
- }
- res.push(index_ty);
- }
-
if recurse >= 10 {
// FIXME: remove this whole recurse thing when the recursion bug is fixed
// See #59502 for the original issue.
@@ -503,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
// for its bounds.
if let Type::Generic(arg_s) = *arg {
// First we check if the bounds are in a `where` predicate...
+ let mut type_bounds = Vec::new();
for where_pred in generics.where_predicates.iter().filter(|g| match g {
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
_ => false,
}) {
- let mut ty_generics = Vec::new();
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
for bound in bounds.iter() {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
}
// Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
- let mut ty_generics = Vec::new();
for bound in bound.get_bounds().unwrap_or(&[]) {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ }
+ if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+ res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+ } else {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
}
} else if let Type::ImplTrait(ref bounds) = *arg {
- let mut ty_generics = Vec::new();
+ let mut type_bounds = Vec::new();
for bound in bounds {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ if is_return && !type_bounds.is_empty() {
+ // In parameter position, `impl Trait` is a unique thing.
+ res.push(RenderType { id: None, generics: Some(type_bounds) });
+ } else {
+ // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ }
} else if let Type::Slice(ref ty) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
} else if let Type::Array(ref ty, _) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
+ } else if let Type::Tuple(ref tys) = *arg {
+ let mut ty_generics = Vec::new();
+ for ty in tys {
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ res.push(get_index_type(arg, ty_generics));
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -594,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
let mut ty_generics = Vec::new();
if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() {
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
gen,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ let id = get_index_type_id(&arg);
+ if id.is_some() || !ty_generics.is_empty() {
+ res.push(RenderType {
+ id,
+ generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+ });
+ }
}
}
@@ -618,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
let decl = &func.decl;
let combined_generics;
@@ -644,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
(None, &func.generics)
};
- let mut all_types = Vec::new();
+ let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+ let mut arg_types = Vec::new();
for arg in decl.inputs.values.iter() {
- let mut args = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
- if !args.is_empty() {
- all_types.extend(args);
- } else {
- all_types.push(get_index_type(&arg.type_, vec![]));
- }
+ simplify_fn_type(
+ self_,
+ generics,
+ &arg.type_,
+ tcx,
+ 0,
+ &mut arg_types,
+ &mut rgen,
+ false,
+ cache,
+ );
}
let mut ret_types = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
- if ret_types.is_empty() {
- ret_types.push(get_index_type(&decl.output, vec![]));
- }
- (all_types, ret_types)
+ simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+ let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+ simplified_params.sort_by_key(|(idx, _)| -idx);
+ (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
}
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index f3da61056..76f63c6f6 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
clean::UnionItem(ref u) => sidebar_union(cx, it, u),
clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
- clean::TypedefItem(_) => sidebar_typedef(cx, it),
+ clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t),
clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
_ => vec![],
@@ -100,7 +100,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_union()
|| it.is_enum()
|| it.is_mod()
- || it.is_typedef()
+ || it.is_type_alias()
{
(
match *it.kind {
@@ -230,8 +230,33 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBl
}
}
-fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+fn sidebar_type_alias<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ t: &'a clean::TypeAlias,
+) -> 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")));
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
+ let mut variants = variants
+ .iter()
+ .filter(|i| !i.is_stripped())
+ .filter_map(|v| v.name)
+ .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+ .collect::<Vec<_>>();
+ variants.sort_unstable();
+
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), 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));
+ }
+ }
+ }
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -254,11 +279,12 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let cache = cx.cache();
+ let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
+ let v = v.as_slice();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if let Some(v) = cache.impls.get(&did) {
+ if !v.is_empty() {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -294,7 +320,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v,
+ v.iter().copied(),
&mut derefs,
&mut used_links,
);
@@ -324,7 +350,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: &[Impl],
+ v: impl Iterator<Item = &'a Impl>,
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -334,7 +360,7 @@ fn sidebar_deref_methods<'a>(
if let Some((target, real_target)) =
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -349,7 +375,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
let inner_impl = target
.def_id(c)
.or_else(|| {
@@ -400,7 +426,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls,
+ target_impls.iter(),
derefs,
used_links,
);