1 use std::collections::hash_map::Entry;
2 use std::collections::BTreeMap;
4 use rustc_data_structures::fx::FxHashMap;
5 use rustc_middle::ty::TyCtxt;
6 use rustc_span::symbol::{kw, Symbol};
7 use serde::ser::{Serialize, SerializeStruct, Serializer};
10 use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate};
11 use crate::formats::cache::Cache;
12 use crate::formats::item_type::ItemType;
13 use crate::html::format::join_with_double_colon;
14 use crate::html::markdown::short_markdown_summary;
15 use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
17 /// Builds the search index from the collected metadata
18 crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
19 let mut defid_to_pathid = FxHashMap::default();
20 let mut crate_paths = vec![];
22 // Attach all orphan items to the type's definition if the type
23 // has since been learned.
24 for &(did, ref item) in &cache.orphan_impl_items {
25 if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
28 .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
29 cache.search_index.push(IndexItem {
31 name: item.name.unwrap().to_string(),
32 path: join_with_double_colon(&fqp[..fqp.len() - 1]),
36 search_type: get_function_type_for_search(item, tcx, &cache),
37 aliases: item.attrs.get_doc_aliases(),
45 .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
47 let Cache { ref mut search_index, ref paths, .. } = *cache;
49 // Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
50 // we need the alias element to have an array of items.
51 let mut aliases: BTreeMap<String, Vec<usize>> = BTreeMap::new();
53 // Sort search index items. This improves the compressibility of the search index.
54 search_index.sort_unstable_by(|k1, k2| {
55 // `sort_unstable_by_key` produces lifetime errors
56 let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
57 let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
58 std::cmp::Ord::cmp(&k1, &k2)
61 // Set up alias indexes.
62 for (i, item) in search_index.iter().enumerate() {
63 for alias in &item.aliases[..] {
64 aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
68 // Reduce `DefId` in paths into smaller sequential numbers,
69 // and prune the paths that do not appear in the index.
70 let mut lastpath = "";
71 let mut lastpathid = 0usize;
73 let crate_items: Vec<&IndexItem> = search_index
76 item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
77 Entry::Occupied(entry) => Some(*entry.get()),
78 Entry::Vacant(entry) => {
79 let pathid = lastpathid;
83 if let Some(&(ref fqp, short)) = paths.get(&defid) {
84 crate_paths.push((short, fqp.last().unwrap().clone()));
92 // Omit the parent path if it is same to that of the prior item.
93 if lastpath == &item.path {
96 lastpath = &item.path;
103 struct CrateData<'a> {
105 items: Vec<&'a IndexItem>,
106 paths: Vec<(ItemType, Symbol)>,
107 // The String is alias name and the vec is the list of the elements with this alias.
109 // To be noted: the `usize` elements are indexes to `items`.
110 aliases: &'a BTreeMap<String, Vec<usize>>,
113 impl<'a> Serialize for CrateData<'a> {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118 let has_aliases = !self.aliases.is_empty();
120 serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
121 crate_data.serialize_field("doc", &self.doc)?;
122 crate_data.serialize_field(
124 &self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
126 crate_data.serialize_field(
128 &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
130 crate_data.serialize_field(
132 &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
134 crate_data.serialize_field(
136 &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
138 crate_data.serialize_field(
145 item.parent.is_some(),
146 item.parent_idx.is_some(),
147 "`{}` is missing idx",
150 item.parent_idx.map(|x| x + 1).unwrap_or(0)
152 .collect::<Vec<_>>(),
154 crate_data.serialize_field(
156 &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
158 crate_data.serialize_field(
160 &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
163 crate_data.serialize_field("a", &self.aliases)?;
169 // Collect the index into a string
173 serde_json::to_string(&CrateData {
179 .expect("failed serde conversion")
180 // All these `replace` calls are because we have to go through JS string for JSON content.
181 .replace(r#"\"#, r"\\")
182 .replace(r#"'"#, r"\'")
183 // We need to escape double quotes for the JSON.
184 .replace("\\\"", "\\\\\"")
188 crate fn get_function_type_for_search<'tcx>(
192 ) -> Option<IndexItemFunctionType> {
193 let (mut inputs, mut output) = match *item.kind {
194 clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, cache),
195 clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, cache),
196 clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, cache),
200 inputs.retain(|a| a.ty.name.is_some());
201 output.retain(|a| a.ty.name.is_some());
203 Some(IndexItemFunctionType { inputs, output })
206 fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
208 name: get_index_type_name(clean_type).map(|s| s.as_str().to_ascii_lowercase()),
209 generics: if generics.is_empty() { None } else { Some(generics) },
213 fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
215 clean::Type::Path { ref path, .. } => {
216 let path_segment = path.segments.last().unwrap();
217 Some(path_segment.name)
219 clean::DynTrait(ref bounds, _) => {
220 let path = &bounds[0].trait_;
221 Some(path.segments.last().unwrap().name)
223 // We return an empty name because we don't care about the generic name itself.
224 clean::Generic(_) => Some(kw::Empty),
225 clean::Primitive(ref p) => Some(p.as_sym()),
226 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_),
227 clean::BareFunction(_)
231 | clean::RawPointer(_, _)
232 | clean::QPath { .. }
234 | clean::ImplTrait(_) => None,
238 /// The point of this function is to replace bounds with types.
240 /// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
241 /// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
243 /// Important note: It goes through generics recursively. So if you have
244 /// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
245 #[instrument(level = "trace", skip(tcx, res, cache))]
246 fn add_generics_and_bounds_as_types<'tcx>(
251 res: &mut Vec<TypeWithKind>,
255 res: &mut Vec<TypeWithKind>,
258 mut generics: Vec<TypeWithKind>,
261 let is_full_generic = ty.is_full_generic();
262 let generics_empty = generics.is_empty();
266 // This is a type parameter with no trait bounds (for example: `T` in
267 // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
268 // with an empty type with an empty name. Let's just discard it.
270 } else if generics.len() == 1 {
271 // In this case, no need to go through an intermediate state if the type parameter
272 // contains only one trait bound.
276 // `fn foo<T: Display>(r: Option<T>) {}`
278 // In this case, it would contain:
293 // After removing the intermediate (unnecessary) type parameter, it'll become:
305 // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
306 // need to keep it as is!
307 res.push(generics.pop().unwrap());
311 let mut index_ty = get_index_type(&ty, generics);
312 if index_ty.name.as_ref().map(|s| s.is_empty() && generics_empty).unwrap_or(true) {
316 // We remove the name of the full generic because we have no use for it.
317 index_ty.name = Some(String::new());
318 res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
319 } else if let Some(kind) = ty.def_id(cache).map(|did| tcx.def_kind(did).into()) {
320 res.push(TypeWithKind::from((index_ty, kind)));
321 } else if ty.is_primitive() {
322 // This is a primitive, let's store it as such.
323 res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
328 // FIXME: remove this whole recurse thing when the recursion bug is fixed
329 // See #59502 for the original issue.
333 // If this argument is a type parameter and not a trait bound or a type, we need to look
335 if let Type::Generic(arg_s) = *arg {
336 // First we check if the bounds are in a `where` predicate...
337 if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
338 WherePredicate::BoundPredicate { ty, .. } => ty.def_id(cache) == arg.def_id(cache),
341 let mut ty_generics = Vec::new();
342 let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
343 for bound in bounds.iter() {
344 if let GenericBound::TraitBound(poly_trait, _) = bound {
345 for param_def in poly_trait.generic_params.iter() {
346 match ¶m_def.kind {
347 clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
348 add_generics_and_bounds_as_types(
362 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
364 // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
365 if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
366 let mut ty_generics = Vec::new();
367 for bound in bound.get_bounds().unwrap_or(&[]) {
368 if let Some(path) = bound.get_trait_path() {
369 let ty = Type::Path { path };
370 add_generics_and_bounds_as_types(
380 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
383 // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
384 // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
386 // So in here, we can add it directly and look for its own type parameters (so for `Option`,
387 // we will look for them but not for `T`).
388 let mut ty_generics = Vec::new();
389 if let Some(arg_generics) = arg.generics() {
390 for gen in arg_generics.iter() {
391 add_generics_and_bounds_as_types(
401 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
405 /// Return the full list of types when bounds have been resolved.
407 /// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
408 /// `[u32, Display, Option]`.
409 fn get_fn_inputs_and_outputs<'tcx>(
413 ) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
414 let decl = &func.decl;
415 let generics = &func.generics;
417 let mut all_types = Vec::new();
418 for arg in decl.inputs.values.iter() {
419 if arg.type_.is_self_type() {
422 let mut args = Vec::new();
423 add_generics_and_bounds_as_types(generics, &arg.type_, tcx, 0, &mut args, cache);
424 if !args.is_empty() {
425 all_types.extend(args);
427 if let Some(kind) = arg.type_.def_id(cache).map(|did| tcx.def_kind(did).into()) {
428 all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
433 let mut ret_types = Vec::new();
435 FnRetTy::Return(ref return_type) => {
436 add_generics_and_bounds_as_types(generics, return_type, tcx, 0, &mut ret_types, cache);
437 if ret_types.is_empty() {
438 if let Some(kind) = return_type.def_id(cache).map(|did| tcx.def_kind(did).into()) {
439 ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
445 (all_types, ret_types)