use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::{kw, Symbol};
+use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::symbol::Symbol;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use crate::clean;
-use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate};
+use crate::clean::types::{
+ FnRetTy, Function, GenericBound, Generics, ItemId, Type, WherePredicate,
+};
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, TypeWithKind};
+use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
/// Builds the search index from the collected metadata
pub(crate) fn build_index<'tcx>(
cache: &mut Cache,
tcx: TyCtxt<'tcx>,
) -> String {
- let mut defid_to_pathid = FxHashMap::default();
+ let mut itemid_to_pathid = FxHashMap::default();
let mut crate_paths = vec![];
// Attach all orphan items to the type's definition if the type
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
- let Cache { ref mut search_index, ref paths, .. } = *cache;
-
// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
// we need the alias element to have an array of items.
let mut aliases: BTreeMap<String, Vec<usize>> = BTreeMap::new();
// Sort search index items. This improves the compressibility of the search index.
- search_index.sort_unstable_by(|k1, k2| {
+ cache.search_index.sort_unstable_by(|k1, k2| {
// `sort_unstable_by_key` produces lifetime errors
let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
});
// Set up alias indexes.
- for (i, item) in search_index.iter().enumerate() {
+ for (i, item) in cache.search_index.iter().enumerate() {
for alias in &item.aliases[..] {
aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
}
let mut lastpath = "";
let mut lastpathid = 0usize;
+ // 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 convert_render_type(
+ ty: &mut RenderType,
+ cache: &mut Cache,
+ itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
+ lastpathid: &mut usize,
+ crate_paths: &mut Vec<(ItemType, Symbol)>,
+ ) {
+ if let Some(generics) = &mut ty.generics {
+ for item in generics {
+ convert_render_type(item, cache, itemid_to_pathid, lastpathid, crate_paths);
+ }
+ }
+ let Cache { ref paths, ref external_paths, .. } = *cache;
+ let Some(id) = ty.id.clone() else {
+ assert!(ty.generics.is_some());
+ return;
+ };
+ let (itemid, path, item_type) = match id {
+ RenderTypeId::DefId(defid) => {
+ if let Some(&(ref fqp, item_type)) =
+ paths.get(&defid).or_else(|| external_paths.get(&defid))
+ {
+ (ItemId::DefId(defid), *fqp.last().unwrap(), item_type)
+ } else {
+ ty.id = None;
+ return;
+ }
+ }
+ RenderTypeId::Primitive(primitive) => (
+ ItemId::Primitive(primitive, LOCAL_CRATE),
+ primitive.as_sym(),
+ ItemType::Primitive,
+ ),
+ RenderTypeId::Index(_) => return,
+ };
+ match itemid_to_pathid.entry(itemid) {
+ Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
+ Entry::Vacant(entry) => {
+ let pathid = *lastpathid;
+ entry.insert(pathid);
+ *lastpathid += 1;
+ crate_paths.push((item_type, path));
+ ty.id = Some(RenderTypeId::Index(pathid));
+ }
+ }
+ }
+ if let Some(search_type) = &mut item.search_type {
+ for item in &mut search_type.inputs {
+ convert_render_type(
+ item,
+ cache,
+ &mut itemid_to_pathid,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ for item in &mut search_type.output {
+ convert_render_type(
+ item,
+ cache,
+ &mut itemid_to_pathid,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ }
+ }
+
+ let Cache { ref paths, .. } = *cache;
+
+ // Then, on parent modules
let crate_items: Vec<&IndexItem> = search_index
.iter_mut()
.map(|item| {
- item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
- Entry::Occupied(entry) => Some(*entry.get()),
- Entry::Vacant(entry) => {
- let pathid = lastpathid;
- entry.insert(pathid);
- lastpathid += 1;
+ item.parent_idx =
+ item.parent.and_then(|defid| match itemid_to_pathid.entry(ItemId::DefId(defid)) {
+ Entry::Occupied(entry) => Some(*entry.get()),
+ Entry::Vacant(entry) => {
+ let pathid = lastpathid;
+ entry.insert(pathid);
+ lastpathid += 1;
- if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, *fqp.last().unwrap()));
- Some(pathid)
- } else {
- None
+ if let Some(&(ref fqp, short)) = paths.get(&defid) {
+ crate_paths.push((short, *fqp.last().unwrap()));
+ Some(pathid)
+ } else {
+ None
+ }
}
- }
- });
+ });
// Omit the parent path if it is same to that of the prior item.
if lastpath == &item.path {
"`{}` 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| &item.search_type).collect::<Vec<_>>(),
+ &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(
"p",
_ => return None,
};
- inputs.retain(|a| a.ty.name.is_some());
- output.retain(|a| a.ty.name.is_some());
+ 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 })
}
-fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
+fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
RenderType {
- name: get_index_type_name(clean_type).map(|s| s.as_str().to_ascii_lowercase()),
+ id: get_index_type_id(clean_type),
generics: if generics.is_empty() { None } else { Some(generics) },
}
}
-fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
+fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
match *clean_type {
- clean::Type::Path { ref path, .. } => {
- let path_segment = path.segments.last().unwrap();
- Some(path_segment.name)
- }
+ clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())),
clean::DynTrait(ref bounds, _) => {
let path = &bounds[0].trait_;
- Some(path.segments.last().unwrap().name)
+ Some(RenderTypeId::DefId(path.def_id()))
}
- // We return an empty name because we don't care about the generic name itself.
- clean::Generic(_) | clean::ImplTrait(_) => Some(kw::Empty),
- clean::Primitive(ref p) => Some(p.as_sym()),
+ clean::Primitive(p) => Some(RenderTypeId::Primitive(p)),
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
- get_index_type_name(type_)
+ get_index_type_id(type_)
}
clean::BareFunction(_)
+ | clean::Generic(_)
+ | clean::ImplTrait(_)
| clean::Tuple(_)
| clean::Slice(_)
| clean::Array(_, _)
arg: &'a Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
- res: &mut Vec<TypeWithKind>,
+ res: &mut Vec<RenderType>,
cache: &Cache,
) {
- fn insert_ty(
- res: &mut Vec<TypeWithKind>,
- tcx: TyCtxt<'_>,
- ty: Type,
- mut generics: Vec<TypeWithKind>,
- 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();
return;
}
}
- let mut index_ty = get_index_type(&ty, generics);
- if index_ty.name.as_ref().map(|s| s.is_empty() && generics_empty).unwrap_or(true) {
+ let index_ty = get_index_type(&ty, generics);
+ if index_ty.id.is_none() && generics_empty {
return;
}
- if anonymous {
- // We remove the name of the full generic because we have no use for it.
- index_ty.name = Some(String::new());
- res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
- } else if let Some(kind) = ty.def_id(cache).map(|did| tcx.def_kind(did).into()) {
- res.push(TypeWithKind::from((index_ty, kind)));
- } else if ty.is_primitive() {
- // This is a primitive, let's store it as such.
- res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
- }
+ res.push(index_ty);
}
if recurse >= 10 {
}
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics, 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) {
);
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+ insert_ty(res, arg.clone(), ty_generics);
}
} else if let Type::ImplTrait(ref bounds) = *arg {
let mut ty_generics = Vec::new();
);
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+ insert_ty(res, arg.clone(), 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.
);
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics, cache);
+ insert_ty(res, arg.clone(), ty_generics);
}
}
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
-) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
+) -> (Vec<RenderType>, Vec<RenderType>) {
let decl = &func.decl;
let combined_generics;
if !args.is_empty() {
all_types.extend(args);
} else {
- if let Some(kind) = arg.type_.def_id(cache).map(|did| tcx.def_kind(did).into()) {
- all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
- }
+ all_types.push(get_index_type(&arg.type_, vec![]));
}
}
cache,
);
if ret_types.is_empty() {
- if let Some(kind) = return_type.def_id(cache).map(|did| tcx.def_kind(did).into()) {
- ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
- }
+ ret_types.push(get_index_type(return_type, vec![]));
}
}
_ => {}