]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render/search_index.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / librustdoc / html / render / search_index.rs
CommitLineData
3c0e092e 1use std::collections::hash_map::Entry;
dfeec247 2use std::collections::BTreeMap;
60c5eb7d 3
3c0e092e 4use rustc_data_structures::fx::FxHashMap;
6a06907d 5use rustc_middle::ty::TyCtxt;
5099ac24 6use rustc_span::symbol::{kw, Symbol};
6a06907d 7use serde::ser::{Serialize, SerializeStruct, Serializer};
e74abb32 8
cdc7bbd5 9use crate::clean;
a2a8927a 10use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate};
923072b8 11use crate::formats::cache::{Cache, OrphanImplItem};
3dfed10e 12use crate::formats::item_type::ItemType;
5099ac24 13use crate::html::format::join_with_double_colon;
fc512014 14use crate::html::markdown::short_markdown_summary;
136023e0 15use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
e74abb32 16
e74abb32 17/// Builds the search index from the collected metadata
923072b8
FG
18pub(crate) fn build_index<'tcx>(
19 krate: &clean::Crate,
20 cache: &mut Cache,
21 tcx: TyCtxt<'tcx>,
22) -> String {
74b04a01 23 let mut defid_to_pathid = FxHashMap::default();
60c5eb7d 24 let mut crate_paths = vec![];
e74abb32 25
e74abb32
XL
26 // Attach all orphan items to the type's definition if the type
27 // has since been learned.
923072b8
FG
28 for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
29 if let Some(&(ref fqp, _)) = cache.paths.get(&parent) {
136023e0
XL
30 let desc = item
31 .doc_value()
3c0e092e 32 .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
5869c6ff 33 cache.search_index.push(IndexItem {
e74abb32 34 ty: item.type_(),
fc512014 35 name: item.name.unwrap().to_string(),
5099ac24 36 path: join_with_double_colon(&fqp[..fqp.len() - 1]),
136023e0 37 desc,
923072b8 38 parent: Some(parent),
e74abb32 39 parent_idx: None,
923072b8 40 search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
cdc7bbd5 41 aliases: item.attrs.get_doc_aliases(),
e74abb32
XL
42 });
43 }
44 }
45
136023e0
XL
46 let crate_doc = krate
47 .module
48 .doc_value()
3c0e092e 49 .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
136023e0 50
cdc7bbd5
XL
51 let Cache { ref mut search_index, ref paths, .. } = *cache;
52
53 // Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
54 // we need the alias element to have an array of items.
55 let mut aliases: BTreeMap<String, Vec<usize>> = BTreeMap::new();
56
57 // Sort search index items. This improves the compressibility of the search index.
58 search_index.sort_unstable_by(|k1, k2| {
59 // `sort_unstable_by_key` produces lifetime errors
60 let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
61 let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
62 std::cmp::Ord::cmp(&k1, &k2)
63 });
64
65 // Set up alias indexes.
66 for (i, item) in search_index.iter().enumerate() {
67 for alias in &item.aliases[..] {
3c0e092e 68 aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
cdc7bbd5
XL
69 }
70 }
5869c6ff 71
74b04a01 72 // Reduce `DefId` in paths into smaller sequential numbers,
e74abb32 73 // and prune the paths that do not appear in the index.
3c0e092e 74 let mut lastpath = "";
e74abb32
XL
75 let mut lastpathid = 0usize;
76
3c0e092e
XL
77 let crate_items: Vec<&IndexItem> = search_index
78 .iter_mut()
79 .map(|item| {
80 item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
81 Entry::Occupied(entry) => Some(*entry.get()),
82 Entry::Vacant(entry) => {
83 let pathid = lastpathid;
84 entry.insert(pathid);
85 lastpathid += 1;
e74abb32 86
3c0e092e 87 if let Some(&(ref fqp, short)) = paths.get(&defid) {
5099ac24 88 crate_paths.push((short, *fqp.last().unwrap()));
3c0e092e
XL
89 Some(pathid)
90 } else {
91 None
92 }
ba9703b0 93 }
3c0e092e
XL
94 });
95
96 // Omit the parent path if it is same to that of the prior item.
97 if lastpath == &item.path {
98 item.path.clear();
99 } else {
100 lastpath = &item.path;
e74abb32 101 }
e74abb32 102
3c0e092e
XL
103 &*item
104 })
105 .collect();
e74abb32 106
60c5eb7d
XL
107 struct CrateData<'a> {
108 doc: String,
60c5eb7d 109 items: Vec<&'a IndexItem>,
5099ac24 110 paths: Vec<(ItemType, Symbol)>,
f9f354fc
XL
111 // The String is alias name and the vec is the list of the elements with this alias.
112 //
113 // To be noted: the `usize` elements are indexes to `items`.
f9f354fc 114 aliases: &'a BTreeMap<String, Vec<usize>>,
60c5eb7d 115 }
e74abb32 116
6a06907d
XL
117 impl<'a> Serialize for CrateData<'a> {
118 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119 where
120 S: Serializer,
121 {
122 let has_aliases = !self.aliases.is_empty();
123 let mut crate_data =
124 serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
125 crate_data.serialize_field("doc", &self.doc)?;
126 crate_data.serialize_field(
127 "t",
128 &self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
129 )?;
130 crate_data.serialize_field(
131 "n",
132 &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
133 )?;
134 crate_data.serialize_field(
135 "q",
136 &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
137 )?;
138 crate_data.serialize_field(
139 "d",
140 &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
141 )?;
142 crate_data.serialize_field(
143 "i",
144 &self
145 .items
146 .iter()
147 .map(|item| {
148 assert_eq!(
149 item.parent.is_some(),
150 item.parent_idx.is_some(),
151 "`{}` is missing idx",
152 item.name
153 );
154 item.parent_idx.map(|x| x + 1).unwrap_or(0)
155 })
156 .collect::<Vec<_>>(),
157 )?;
158 crate_data.serialize_field(
159 "f",
160 &self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
161 )?;
5099ac24
FG
162 crate_data.serialize_field(
163 "p",
164 &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
165 )?;
6a06907d
XL
166 if has_aliases {
167 crate_data.serialize_field("a", &self.aliases)?;
168 }
169 crate_data.end()
170 }
171 }
172
e74abb32 173 // Collect the index into a string
60c5eb7d 174 format!(
ba9703b0 175 r#""{}":{}"#,
3c0e092e 176 krate.name(tcx),
60c5eb7d
XL
177 serde_json::to_string(&CrateData {
178 doc: crate_doc,
179 items: crate_items,
180 paths: crate_paths,
cdc7bbd5 181 aliases: &aliases,
60c5eb7d 182 })
dfeec247 183 .expect("failed serde conversion")
ba9703b0 184 // All these `replace` calls are because we have to go through JS string for JSON content.
923072b8
FG
185 .replace('\\', r"\\")
186 .replace('\'', r"\'")
ba9703b0
XL
187 // We need to escape double quotes for the JSON.
188 .replace("\\\"", "\\\\\"")
60c5eb7d 189 )
e74abb32
XL
190}
191
923072b8 192pub(crate) fn get_function_type_for_search<'tcx>(
5869c6ff 193 item: &clean::Item,
6a06907d 194 tcx: TyCtxt<'tcx>,
923072b8 195 impl_generics: Option<&(clean::Type, clean::Generics)>,
5099ac24 196 cache: &Cache,
5869c6ff 197) -> Option<IndexItemFunctionType> {
3c0e092e 198 let (mut inputs, mut output) = match *item.kind {
923072b8
FG
199 clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
200 clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
201 clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
e74abb32
XL
202 _ => return None,
203 };
204
3c0e092e
XL
205 inputs.retain(|a| a.ty.name.is_some());
206 output.retain(|a| a.ty.name.is_some());
e74abb32
XL
207
208 Some(IndexItemFunctionType { inputs, output })
209}
210
3c0e092e 211fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
ba9703b0 212 RenderType {
a2a8927a 213 name: get_index_type_name(clean_type).map(|s| s.as_str().to_ascii_lowercase()),
3c0e092e 214 generics: if generics.is_empty() { None } else { Some(generics) },
ba9703b0 215 }
e74abb32
XL
216}
217
a2a8927a 218fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
e74abb32 219 match *clean_type {
3c0e092e 220 clean::Type::Path { ref path, .. } => {
c295e0f8 221 let path_segment = path.segments.last().unwrap();
fc512014 222 Some(path_segment.name)
e74abb32 223 }
c295e0f8
XL
224 clean::DynTrait(ref bounds, _) => {
225 let path = &bounds[0].trait_;
226 Some(path.segments.last().unwrap().name)
227 }
5099ac24 228 // We return an empty name because we don't care about the generic name itself.
923072b8 229 clean::Generic(_) | clean::ImplTrait(_) => Some(kw::Empty),
fc512014 230 clean::Primitive(ref p) => Some(p.as_sym()),
923072b8
FG
231 clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
232 get_index_type_name(type_)
233 }
a2a8927a 234 clean::BareFunction(_)
5869c6ff
XL
235 | clean::Tuple(_)
236 | clean::Slice(_)
237 | clean::Array(_, _)
5869c6ff 238 | clean::QPath { .. }
923072b8 239 | clean::Infer => None,
e74abb32
XL
240 }
241}
242
6a06907d
XL
243/// The point of this function is to replace bounds with types.
244///
245/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
3c0e092e
XL
246/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
247///
248/// Important note: It goes through generics recursively. So if you have
249/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
5099ac24 250#[instrument(level = "trace", skip(tcx, res, cache))]
923072b8
FG
251fn add_generics_and_bounds_as_types<'tcx, 'a>(
252 self_: Option<&'a Type>,
6a06907d 253 generics: &Generics,
923072b8 254 arg: &'a Type,
6a06907d 255 tcx: TyCtxt<'tcx>,
3c0e092e
XL
256 recurse: usize,
257 res: &mut Vec<TypeWithKind>,
5099ac24 258 cache: &Cache,
3c0e092e
XL
259) {
260 fn insert_ty(
261 res: &mut Vec<TypeWithKind>,
262 tcx: TyCtxt<'_>,
263 ty: Type,
264 mut generics: Vec<TypeWithKind>,
5099ac24 265 cache: &Cache,
3c0e092e 266 ) {
923072b8
FG
267 // generics and impl trait are both identified by their generics,
268 // rather than a type name itself
269 let anonymous = ty.is_full_generic() || ty.is_impl_trait();
5099ac24 270 let generics_empty = generics.is_empty();
3c0e092e 271
923072b8 272 if anonymous {
5099ac24 273 if generics_empty {
3c0e092e
XL
274 // This is a type parameter with no trait bounds (for example: `T` in
275 // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
276 // with an empty type with an empty name. Let's just discard it.
277 return;
278 } else if generics.len() == 1 {
279 // In this case, no need to go through an intermediate state if the type parameter
280 // contains only one trait bound.
281 //
282 // For example:
283 //
284 // `fn foo<T: Display>(r: Option<T>) {}`
285 //
286 // In this case, it would contain:
287 //
288 // ```
289 // [{
290 // name: "option",
291 // generics: [{
292 // name: "",
293 // generics: [
294 // name: "Display",
295 // generics: []
296 // }]
297 // }]
298 // }]
299 // ```
300 //
301 // After removing the intermediate (unnecessary) type parameter, it'll become:
302 //
303 // ```
304 // [{
305 // name: "option",
306 // generics: [{
307 // name: "Display",
308 // generics: []
309 // }]
310 // }]
311 // ```
312 //
313 // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
314 // need to keep it as is!
315 res.push(generics.pop().unwrap());
316 return;
317 }
318 }
319 let mut index_ty = get_index_type(&ty, generics);
5099ac24 320 if index_ty.name.as_ref().map(|s| s.is_empty() && generics_empty).unwrap_or(true) {
3c0e092e
XL
321 return;
322 }
923072b8 323 if anonymous {
3c0e092e
XL
324 // We remove the name of the full generic because we have no use for it.
325 index_ty.name = Some(String::new());
326 res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
5099ac24 327 } else if let Some(kind) = ty.def_id(cache).map(|did| tcx.def_kind(did).into()) {
3c0e092e 328 res.push(TypeWithKind::from((index_ty, kind)));
6a06907d
XL
329 } else if ty.is_primitive() {
330 // This is a primitive, let's store it as such.
3c0e092e 331 res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
6a06907d
XL
332 }
333 }
334
335 if recurse >= 10 {
336 // FIXME: remove this whole recurse thing when the recursion bug is fixed
a2a8927a 337 // See #59502 for the original issue.
3c0e092e 338 return;
6a06907d 339 }
6a06907d 340
923072b8
FG
341 // First, check if it's "Self".
342 let arg = if let Some(self_) = self_ {
343 match &*arg {
344 Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_,
345 type_ if type_.is_self_type() => self_,
346 arg => arg,
347 }
348 } else {
349 arg
350 };
351
3c0e092e
XL
352 // If this argument is a type parameter and not a trait bound or a type, we need to look
353 // for its bounds.
354 if let Type::Generic(arg_s) = *arg {
355 // First we check if the bounds are in a `where` predicate...
6a06907d 356 if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
5099ac24 357 WherePredicate::BoundPredicate { ty, .. } => ty.def_id(cache) == arg.def_id(cache),
6a06907d
XL
358 _ => false,
359 }) {
3c0e092e 360 let mut ty_generics = Vec::new();
6a06907d
XL
361 let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
362 for bound in bounds.iter() {
363 if let GenericBound::TraitBound(poly_trait, _) = bound {
a2a8927a
XL
364 for param_def in poly_trait.generic_params.iter() {
365 match &param_def.kind {
366 clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
367 add_generics_and_bounds_as_types(
923072b8 368 self_,
a2a8927a
XL
369 generics,
370 ty,
371 tcx,
372 recurse + 1,
373 &mut ty_generics,
5099ac24 374 cache,
a2a8927a
XL
375 )
376 }
377 _ => {}
6a06907d
XL
378 }
379 }
380 }
381 }
5099ac24 382 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
6a06907d 383 }
3c0e092e 384 // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
6a06907d 385 if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
3c0e092e 386 let mut ty_generics = Vec::new();
6a06907d 387 for bound in bound.get_bounds().unwrap_or(&[]) {
c295e0f8 388 if let Some(path) = bound.get_trait_path() {
3c0e092e 389 let ty = Type::Path { path };
a2a8927a 390 add_generics_and_bounds_as_types(
923072b8 391 self_,
a2a8927a
XL
392 generics,
393 &ty,
394 tcx,
395 recurse + 1,
396 &mut ty_generics,
5099ac24 397 cache,
a2a8927a 398 );
6a06907d
XL
399 }
400 }
5099ac24 401 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
6a06907d 402 }
923072b8
FG
403 } else if let Type::ImplTrait(ref bounds) = *arg {
404 let mut ty_generics = Vec::new();
405 for bound in bounds {
406 if let Some(path) = bound.get_trait_path() {
407 let ty = Type::Path { path };
408 add_generics_and_bounds_as_types(
409 self_,
410 generics,
411 &ty,
412 tcx,
413 recurse + 1,
414 &mut ty_generics,
415 cache,
416 );
417 }
418 }
419 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
6a06907d 420 } else {
3c0e092e
XL
421 // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
422 // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
423 //
424 // So in here, we can add it directly and look for its own type parameters (so for `Option`,
425 // we will look for them but not for `T`).
426 let mut ty_generics = Vec::new();
427 if let Some(arg_generics) = arg.generics() {
428 for gen in arg_generics.iter() {
5099ac24 429 add_generics_and_bounds_as_types(
923072b8 430 self_,
5099ac24
FG
431 generics,
432 gen,
433 tcx,
434 recurse + 1,
435 &mut ty_generics,
436 cache,
437 );
6a06907d
XL
438 }
439 }
5099ac24 440 insert_ty(res, tcx, arg.clone(), ty_generics, cache);
6a06907d 441 }
6a06907d
XL
442}
443
444/// Return the full list of types when bounds have been resolved.
445///
446/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
447/// `[u32, Display, Option]`.
a2a8927a
XL
448fn get_fn_inputs_and_outputs<'tcx>(
449 func: &Function,
6a06907d 450 tcx: TyCtxt<'tcx>,
923072b8 451 impl_generics: Option<&(clean::Type, clean::Generics)>,
5099ac24 452 cache: &Cache,
3c0e092e 453) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
a2a8927a 454 let decl = &func.decl;
923072b8
FG
455
456 let combined_generics;
457 let (self_, generics) = if let Some(&(ref impl_self, ref impl_generics)) = impl_generics {
458 match (impl_generics.is_empty(), func.generics.is_empty()) {
459 (true, _) => (Some(impl_self), &func.generics),
460 (_, true) => (Some(impl_self), impl_generics),
461 (false, false) => {
462 let mut params = func.generics.params.clone();
463 params.extend(impl_generics.params.clone());
464 let mut where_predicates = func.generics.where_predicates.clone();
465 where_predicates.extend(impl_generics.where_predicates.clone());
466 combined_generics = clean::Generics { params, where_predicates };
467 (Some(impl_self), &combined_generics)
468 }
469 }
470 } else {
471 (None, &func.generics)
472 };
a2a8927a 473
3c0e092e 474 let mut all_types = Vec::new();
6a06907d 475 for arg in decl.inputs.values.iter() {
3c0e092e 476 let mut args = Vec::new();
923072b8 477 add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
6a06907d
XL
478 if !args.is_empty() {
479 all_types.extend(args);
480 } else {
5099ac24 481 if let Some(kind) = arg.type_.def_id(cache).map(|did| tcx.def_kind(did).into()) {
3c0e092e 482 all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
6a06907d
XL
483 }
484 }
485 }
486
3c0e092e
XL
487 let mut ret_types = Vec::new();
488 match decl.output {
6a06907d 489 FnRetTy::Return(ref return_type) => {
923072b8
FG
490 add_generics_and_bounds_as_types(
491 self_,
492 generics,
493 return_type,
494 tcx,
495 0,
496 &mut ret_types,
497 cache,
498 );
3c0e092e 499 if ret_types.is_empty() {
5099ac24 500 if let Some(kind) = return_type.def_id(cache).map(|did| tcx.def_kind(did).into()) {
3c0e092e 501 ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
6a06907d
XL
502 }
503 }
6a06907d 504 }
3c0e092e 505 _ => {}
6a06907d 506 };
3c0e092e 507 (all_types, ret_types)
6a06907d 508}