]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::clean::{self, AttributesExt, GetDefId}; |
e74abb32 | 2 | use crate::fold::DocFolder; |
e74abb32 | 3 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
dfeec247 | 4 | use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; |
ba9703b0 | 5 | use rustc_middle::middle::privacy::AccessLevels; |
dfeec247 XL |
6 | use rustc_span::source_map::FileName; |
7 | use rustc_span::symbol::sym; | |
8 | use std::collections::BTreeMap; | |
e74abb32 XL |
9 | use std::mem; |
10 | use std::path::{Path, PathBuf}; | |
60c5eb7d XL |
11 | |
12 | use serde::Serialize; | |
e74abb32 | 13 | |
dfeec247 | 14 | use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}; |
ba9703b0 | 15 | use super::{Generic, RenderInfo, RenderType, TypeWithKind}; |
e74abb32 XL |
16 | |
17 | /// Indicates where an external crate can be found. | |
18 | pub enum ExternalLocation { | |
19 | /// Remote URL root of the external crate | |
20 | Remote(String), | |
21 | /// This external crate can be found in the local doc/ folder | |
22 | Local, | |
23 | /// The external crate could not be found. | |
24 | Unknown, | |
25 | } | |
26 | ||
27 | /// This cache is used to store information about the `clean::Crate` being | |
28 | /// rendered in order to provide more useful documentation. This contains | |
29 | /// information like all implementors of a trait, all traits a type implements, | |
30 | /// documentation for all known traits, etc. | |
31 | /// | |
32 | /// This structure purposefully does not implement `Clone` because it's intended | |
33 | /// to be a fairly large and expensive structure to clone. Instead this adheres | |
34 | /// to `Send` so it may be stored in a `Arc` instance and shared among the various | |
35 | /// rendering threads. | |
36 | #[derive(Default)] | |
37 | crate struct Cache { | |
38 | /// Maps a type ID to all known implementations for that type. This is only | |
39 | /// recognized for intra-crate `ResolvedPath` types, and is used to print | |
40 | /// out extra documentation on the page of an enum/struct. | |
41 | /// | |
42 | /// The values of the map are a list of implementations and documentation | |
43 | /// found on that implementation. | |
44 | pub impls: FxHashMap<DefId, Vec<Impl>>, | |
45 | ||
74b04a01 | 46 | /// Maintains a mapping of local crate `DefId`s to the fully qualified name |
e74abb32 XL |
47 | /// and "short type description" of that node. This is used when generating |
48 | /// URLs when a type is being linked to. External paths are not located in | |
49 | /// this map because the `External` type itself has all the information | |
50 | /// necessary. | |
51 | pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>, | |
52 | ||
53 | /// Similar to `paths`, but only holds external paths. This is only used for | |
54 | /// generating explicit hyperlinks to other crates. | |
55 | pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, | |
56 | ||
57 | /// Maps local `DefId`s of exported types to fully qualified paths. | |
58 | /// Unlike 'paths', this mapping ignores any renames that occur | |
59 | /// due to 'use' statements. | |
60 | /// | |
61 | /// This map is used when writing out the special 'implementors' | |
62 | /// javascript file. By using the exact path that the type | |
63 | /// is declared with, we ensure that each path will be identical | |
64 | /// to the path used if the corresponding type is inlined. By | |
65 | /// doing this, we can detect duplicate impls on a trait page, and only display | |
66 | /// the impl for the inlined type. | |
67 | pub exact_paths: FxHashMap<DefId, Vec<String>>, | |
68 | ||
69 | /// This map contains information about all known traits of this crate. | |
70 | /// Implementations of a crate should inherit the documentation of the | |
71 | /// parent trait if no extra documentation is specified, and default methods | |
72 | /// should show up in documentation about trait implementations. | |
73 | pub traits: FxHashMap<DefId, clean::Trait>, | |
74 | ||
75 | /// When rendering traits, it's often useful to be able to list all | |
76 | /// implementors of the trait, and this mapping is exactly, that: a mapping | |
77 | /// of trait ids to the list of known implementors of the trait | |
78 | pub implementors: FxHashMap<DefId, Vec<Impl>>, | |
79 | ||
80 | /// Cache of where external crate documentation can be found. | |
81 | pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>, | |
82 | ||
83 | /// Cache of where documentation for primitives can be found. | |
84 | pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>, | |
85 | ||
86 | // Note that external items for which `doc(hidden)` applies to are shown as | |
87 | // non-reachable while local items aren't. This is because we're reusing | |
88 | // the access levels from the privacy check pass. | |
89 | pub access_levels: AccessLevels<DefId>, | |
90 | ||
91 | /// The version of the crate being documented, if given from the `--crate-version` flag. | |
92 | pub crate_version: Option<String>, | |
93 | ||
94 | // Private fields only used when initially crawling a crate to build a cache | |
e74abb32 XL |
95 | stack: Vec<String>, |
96 | parent_stack: Vec<DefId>, | |
97 | parent_is_trait_impl: bool, | |
98 | search_index: Vec<IndexItem>, | |
99 | stripped_mod: bool, | |
100 | pub deref_trait_did: Option<DefId>, | |
101 | pub deref_mut_trait_did: Option<DefId>, | |
102 | pub owned_box_did: Option<DefId>, | |
103 | masked_crates: FxHashSet<CrateNum>, | |
104 | ||
105 | // In rare case where a structure is defined in one module but implemented | |
106 | // in another, if the implementing module is parsed before defining module, | |
107 | // then the fully qualified name of the structure isn't presented in `paths` | |
108 | // yet when its implementation methods are being indexed. Caches such methods | |
109 | // and their parent id here and indexes them at the end of crate parsing. | |
110 | orphan_impl_items: Vec<(DefId, clean::Item)>, | |
111 | ||
112 | // Similarly to `orphan_impl_items`, sometimes trait impls are picked up | |
113 | // even though the trait itself is not exported. This can happen if a trait | |
114 | // was defined in function/expression scope, since the impl will be picked | |
115 | // up by `collect-trait-impls` but the trait won't be scraped out in the HIR | |
116 | // crawl. In order to prevent crashes when looking for spotlight traits or | |
117 | // when gathering trait documentation on a type, hold impls here while | |
118 | // folding and add them to the cache later on if we find the trait. | |
119 | orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>, | |
120 | ||
121 | /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, | |
122 | /// we need the alias element to have an array of items. | |
f9f354fc | 123 | pub(super) aliases: BTreeMap<String, Vec<usize>>, |
e74abb32 XL |
124 | } |
125 | ||
126 | impl Cache { | |
127 | pub fn from_krate( | |
128 | renderinfo: RenderInfo, | |
129 | extern_html_root_urls: &BTreeMap<String, String>, | |
130 | dst: &Path, | |
131 | mut krate: clean::Crate, | |
132 | ) -> (clean::Crate, String, Cache) { | |
133 | // Crawl the crate to build various caches used for the output | |
134 | let RenderInfo { | |
135 | inlined: _, | |
136 | external_paths, | |
137 | exact_paths, | |
138 | access_levels, | |
139 | deref_trait_did, | |
140 | deref_mut_trait_did, | |
141 | owned_box_did, | |
ba9703b0 | 142 | .. |
e74abb32 XL |
143 | } = renderinfo; |
144 | ||
dfeec247 XL |
145 | let external_paths = |
146 | external_paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from(t)))).collect(); | |
e74abb32 XL |
147 | |
148 | let mut cache = Cache { | |
149 | impls: Default::default(), | |
150 | external_paths, | |
151 | exact_paths, | |
152 | paths: Default::default(), | |
153 | implementors: Default::default(), | |
154 | stack: Vec::new(), | |
155 | parent_stack: Vec::new(), | |
156 | search_index: Vec::new(), | |
157 | parent_is_trait_impl: false, | |
158 | extern_locations: Default::default(), | |
159 | primitive_locations: Default::default(), | |
160 | stripped_mod: false, | |
161 | access_levels, | |
162 | crate_version: krate.version.take(), | |
163 | orphan_impl_items: Vec::new(), | |
164 | orphan_trait_impls: Vec::new(), | |
165 | traits: krate.external_traits.replace(Default::default()), | |
166 | deref_trait_did, | |
167 | deref_mut_trait_did, | |
168 | owned_box_did, | |
169 | masked_crates: mem::take(&mut krate.masked_crates), | |
170 | aliases: Default::default(), | |
171 | }; | |
172 | ||
173 | // Cache where all our extern crates are located | |
174 | for &(n, ref e) in &krate.externs { | |
175 | let src_root = match e.src { | |
ba9703b0 | 176 | FileName::Real(ref p) => match p.local_path().parent() { |
e74abb32 XL |
177 | Some(p) => p.to_path_buf(), |
178 | None => PathBuf::new(), | |
179 | }, | |
180 | _ => PathBuf::new(), | |
181 | }; | |
182 | let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u); | |
dfeec247 XL |
183 | cache |
184 | .extern_locations | |
185 | .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst))); | |
e74abb32 XL |
186 | |
187 | let did = DefId { krate: n, index: CRATE_DEF_INDEX }; | |
188 | cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); | |
189 | } | |
190 | ||
191 | // Cache where all known primitives have their documentation located. | |
192 | // | |
193 | // Favor linking to as local extern as possible, so iterate all crates in | |
194 | // reverse topological order. | |
195 | for &(_, ref e) in krate.externs.iter().rev() { | |
196 | for &(def_id, prim, _) in &e.primitives { | |
197 | cache.primitive_locations.insert(prim, def_id); | |
198 | } | |
199 | } | |
200 | for &(def_id, prim, _) in &krate.primitives { | |
201 | cache.primitive_locations.insert(prim, def_id); | |
202 | } | |
203 | ||
204 | cache.stack.push(krate.name.clone()); | |
205 | krate = cache.fold_crate(krate); | |
206 | ||
207 | for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { | |
208 | if cache.traits.contains_key(&trait_did) { | |
209 | for did in dids { | |
210 | cache.impls.entry(did).or_insert(vec![]).push(impl_.clone()); | |
211 | } | |
212 | } | |
213 | } | |
214 | ||
215 | // Build our search index | |
216 | let index = build_index(&krate, &mut cache); | |
217 | ||
218 | (krate, index, cache) | |
219 | } | |
220 | } | |
221 | ||
222 | impl DocFolder for Cache { | |
223 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | |
224 | if item.def_id.is_local() { | |
225 | debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id); | |
226 | } | |
227 | ||
228 | // If this is a stripped module, | |
229 | // we don't want it or its children in the search index. | |
230 | let orig_stripped_mod = match item.inner { | |
231 | clean::StrippedItem(box clean::ModuleItem(..)) => { | |
232 | mem::replace(&mut self.stripped_mod, true) | |
233 | } | |
234 | _ => self.stripped_mod, | |
235 | }; | |
236 | ||
237 | // If the impl is from a masked crate or references something from a | |
238 | // masked crate then remove it completely. | |
239 | if let clean::ImplItem(ref i) = item.inner { | |
dfeec247 XL |
240 | if self.masked_crates.contains(&item.def_id.krate) |
241 | || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) | |
242 | || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) | |
243 | { | |
e74abb32 XL |
244 | return None; |
245 | } | |
246 | } | |
247 | ||
248 | // Propagate a trait method's documentation to all implementors of the | |
249 | // trait. | |
250 | if let clean::TraitItem(ref t) = item.inner { | |
251 | self.traits.entry(item.def_id).or_insert_with(|| t.clone()); | |
252 | } | |
253 | ||
254 | // Collect all the implementors of traits. | |
255 | if let clean::ImplItem(ref i) = item.inner { | |
256 | if let Some(did) = i.trait_.def_id() { | |
257 | if i.blanket_impl.is_none() { | |
dfeec247 XL |
258 | self.implementors |
259 | .entry(did) | |
260 | .or_default() | |
261 | .push(Impl { impl_item: item.clone() }); | |
e74abb32 XL |
262 | } |
263 | } | |
264 | } | |
265 | ||
266 | // Index this method for searching later on. | |
267 | if let Some(ref s) = item.name { | |
268 | let (parent, is_inherent_impl_item) = match item.inner { | |
269 | clean::StrippedItem(..) => ((None, None), false), | |
dfeec247 XL |
270 | clean::AssocConstItem(..) | clean::TypedefItem(_, true) |
271 | if self.parent_is_trait_impl => | |
272 | { | |
e74abb32 XL |
273 | // skip associated items in trait impls |
274 | ((None, None), false) | |
275 | } | |
dfeec247 XL |
276 | clean::AssocTypeItem(..) |
277 | | clean::TyMethodItem(..) | |
278 | | clean::StructFieldItem(..) | |
279 | | clean::VariantItem(..) => ( | |
280 | ( | |
281 | Some(*self.parent_stack.last().expect("parent_stack is empty")), | |
282 | Some(&self.stack[..self.stack.len() - 1]), | |
283 | ), | |
284 | false, | |
285 | ), | |
e74abb32 XL |
286 | clean::MethodItem(..) | clean::AssocConstItem(..) => { |
287 | if self.parent_stack.is_empty() { | |
288 | ((None, None), false) | |
289 | } else { | |
dfeec247 | 290 | let last = self.parent_stack.last().expect("parent_stack is empty 2"); |
e74abb32 XL |
291 | let did = *last; |
292 | let path = match self.paths.get(&did) { | |
293 | // The current stack not necessarily has correlation | |
294 | // for where the type was defined. On the other | |
295 | // hand, `paths` always has the right | |
296 | // information if present. | |
ba9703b0 XL |
297 | Some(&( |
298 | ref fqp, | |
299 | ItemType::Trait | |
300 | | ItemType::Struct | |
301 | | ItemType::Union | |
302 | | ItemType::Enum, | |
303 | )) => Some(&fqp[..fqp.len() - 1]), | |
e74abb32 | 304 | Some(..) => Some(&*self.stack), |
dfeec247 | 305 | None => None, |
e74abb32 XL |
306 | }; |
307 | ((Some(*last), path), true) | |
308 | } | |
309 | } | |
dfeec247 | 310 | _ => ((None, Some(&*self.stack)), false), |
e74abb32 XL |
311 | }; |
312 | ||
313 | match parent { | |
f9f354fc | 314 | (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { |
e74abb32 XL |
315 | debug_assert!(!item.is_stripped()); |
316 | ||
317 | // A crate has a module at its root, containing all items, | |
318 | // which should not be indexed. The crate-item itself is | |
319 | // inserted later on when serializing the search-index. | |
320 | if item.def_id.index != CRATE_DEF_INDEX { | |
321 | self.search_index.push(IndexItem { | |
322 | ty: item.type_(), | |
323 | name: s.to_string(), | |
324 | path: path.join("::"), | |
325 | desc: shorten(plain_summary_line(item.doc_value())), | |
326 | parent, | |
327 | parent_idx: None, | |
328 | search_type: get_index_search_type(&item), | |
329 | }); | |
f9f354fc XL |
330 | |
331 | for alias in item.attrs.get_doc_aliases() { | |
332 | self.aliases | |
333 | .entry(alias.to_lowercase()) | |
334 | .or_insert(Vec::new()) | |
335 | .push(self.search_index.len() - 1); | |
336 | } | |
e74abb32 XL |
337 | } |
338 | } | |
339 | (Some(parent), None) if is_inherent_impl_item => { | |
340 | // We have a parent, but we don't know where they're | |
341 | // defined yet. Wait for later to index this item. | |
342 | self.orphan_impl_items.push((parent, item.clone())); | |
343 | } | |
344 | _ => {} | |
345 | } | |
346 | } | |
347 | ||
348 | // Keep track of the fully qualified path for this item. | |
349 | let pushed = match item.name { | |
350 | Some(ref n) if !n.is_empty() => { | |
351 | self.stack.push(n.to_string()); | |
352 | true | |
353 | } | |
354 | _ => false, | |
355 | }; | |
356 | ||
357 | match item.inner { | |
dfeec247 XL |
358 | clean::StructItem(..) |
359 | | clean::EnumItem(..) | |
360 | | clean::TypedefItem(..) | |
361 | | clean::TraitItem(..) | |
362 | | clean::FunctionItem(..) | |
363 | | clean::ModuleItem(..) | |
364 | | clean::ForeignFunctionItem(..) | |
365 | | clean::ForeignStaticItem(..) | |
366 | | clean::ConstantItem(..) | |
367 | | clean::StaticItem(..) | |
368 | | clean::UnionItem(..) | |
369 | | clean::ForeignTypeItem | |
370 | | clean::MacroItem(..) | |
371 | | clean::ProcMacroItem(..) | |
74b04a01 | 372 | | clean::VariantItem(..) |
dfeec247 XL |
373 | if !self.stripped_mod => |
374 | { | |
e74abb32 XL |
375 | // Re-exported items mean that the same id can show up twice |
376 | // in the rustdoc ast that we're looking at. We know, | |
377 | // however, that a re-exported item doesn't show up in the | |
378 | // `public_items` map, so we can skip inserting into the | |
379 | // paths map if there was already an entry present and we're | |
380 | // not a public item. | |
dfeec247 XL |
381 | if !self.paths.contains_key(&item.def_id) |
382 | || self.access_levels.is_public(item.def_id) | |
e74abb32 | 383 | { |
dfeec247 | 384 | self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); |
e74abb32 | 385 | } |
e74abb32 | 386 | } |
e74abb32 | 387 | clean::PrimitiveItem(..) => { |
dfeec247 | 388 | self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); |
e74abb32 XL |
389 | } |
390 | ||
391 | _ => {} | |
392 | } | |
393 | ||
394 | // Maintain the parent stack | |
395 | let orig_parent_is_trait_impl = self.parent_is_trait_impl; | |
396 | let parent_pushed = match item.inner { | |
dfeec247 XL |
397 | clean::TraitItem(..) |
398 | | clean::EnumItem(..) | |
399 | | clean::ForeignTypeItem | |
400 | | clean::StructItem(..) | |
74b04a01 XL |
401 | | clean::UnionItem(..) |
402 | | clean::VariantItem(..) => { | |
e74abb32 XL |
403 | self.parent_stack.push(item.def_id); |
404 | self.parent_is_trait_impl = false; | |
405 | true | |
406 | } | |
407 | clean::ImplItem(ref i) => { | |
408 | self.parent_is_trait_impl = i.trait_.is_some(); | |
409 | match i.for_ { | |
dfeec247 | 410 | clean::ResolvedPath { did, .. } => { |
e74abb32 XL |
411 | self.parent_stack.push(did); |
412 | true | |
413 | } | |
414 | ref t => { | |
dfeec247 XL |
415 | let prim_did = t |
416 | .primitive_type() | |
417 | .and_then(|t| self.primitive_locations.get(&t).cloned()); | |
e74abb32 XL |
418 | match prim_did { |
419 | Some(did) => { | |
420 | self.parent_stack.push(did); | |
421 | true | |
422 | } | |
423 | None => false, | |
424 | } | |
425 | } | |
426 | } | |
427 | } | |
dfeec247 | 428 | _ => false, |
e74abb32 XL |
429 | }; |
430 | ||
431 | // Once we've recursively found all the generics, hoard off all the | |
432 | // implementations elsewhere. | |
433 | let ret = self.fold_item_recur(item).and_then(|item| { | |
434 | if let clean::Item { inner: clean::ImplItem(_), .. } = item { | |
435 | // Figure out the id of this impl. This may map to a | |
436 | // primitive rather than always to a struct/enum. | |
437 | // Note: matching twice to restrict the lifetime of the `i` borrow. | |
438 | let mut dids = FxHashSet::default(); | |
439 | if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { | |
440 | match i.for_ { | |
dfeec247 XL |
441 | clean::ResolvedPath { did, .. } |
442 | | clean::BorrowedRef { | |
e74abb32 XL |
443 | type_: box clean::ResolvedPath { did, .. }, .. |
444 | } => { | |
445 | dids.insert(did); | |
446 | } | |
447 | ref t => { | |
dfeec247 XL |
448 | let did = t |
449 | .primitive_type() | |
450 | .and_then(|t| self.primitive_locations.get(&t).cloned()); | |
e74abb32 XL |
451 | |
452 | if let Some(did) = did { | |
453 | dids.insert(did); | |
454 | } | |
455 | } | |
456 | } | |
457 | ||
458 | if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { | |
459 | for bound in generics { | |
460 | if let Some(did) = bound.def_id() { | |
461 | dids.insert(did); | |
462 | } | |
463 | } | |
464 | } | |
465 | } else { | |
466 | unreachable!() | |
467 | }; | |
dfeec247 | 468 | let impl_item = Impl { impl_item: item }; |
e74abb32 XL |
469 | if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { |
470 | for did in dids { | |
471 | self.impls.entry(did).or_insert(vec![]).push(impl_item.clone()); | |
472 | } | |
473 | } else { | |
dfeec247 | 474 | let trait_did = impl_item.trait_did().expect("no trait did"); |
e74abb32 XL |
475 | self.orphan_trait_impls.push((trait_did, dids, impl_item)); |
476 | } | |
477 | None | |
478 | } else { | |
479 | Some(item) | |
480 | } | |
481 | }); | |
482 | ||
dfeec247 XL |
483 | if pushed { |
484 | self.stack.pop().expect("stack already empty"); | |
485 | } | |
486 | if parent_pushed { | |
487 | self.parent_stack.pop().expect("parent stack already empty"); | |
488 | } | |
e74abb32 XL |
489 | self.stripped_mod = orig_stripped_mod; |
490 | self.parent_is_trait_impl = orig_parent_is_trait_impl; | |
491 | ret | |
492 | } | |
493 | } | |
494 | ||
e74abb32 XL |
495 | /// Attempts to find where an external crate is located, given that we're |
496 | /// rendering in to the specified source destination. | |
dfeec247 XL |
497 | fn extern_location( |
498 | e: &clean::ExternalCrate, | |
499 | extern_url: Option<&str>, | |
500 | dst: &Path, | |
501 | ) -> ExternalLocation { | |
e74abb32 XL |
502 | use ExternalLocation::*; |
503 | // See if there's documentation generated into the local directory | |
504 | let local_location = dst.join(&e.name); | |
505 | if local_location.is_dir() { | |
506 | return Local; | |
507 | } | |
508 | ||
509 | if let Some(url) = extern_url { | |
510 | let mut url = url.to_string(); | |
74b04a01 | 511 | if !url.ends_with('/') { |
e74abb32 XL |
512 | url.push('/'); |
513 | } | |
514 | return Remote(url); | |
515 | } | |
516 | ||
517 | // Failing that, see if there's an attribute specifying where to find this | |
518 | // external crate | |
dfeec247 XL |
519 | e.attrs |
520 | .lists(sym::doc) | |
521 | .filter(|a| a.check_name(sym::html_root_url)) | |
522 | .filter_map(|a| a.value_str()) | |
523 | .map(|url| { | |
524 | let mut url = url.to_string(); | |
74b04a01 | 525 | if !url.ends_with('/') { |
dfeec247 XL |
526 | url.push('/') |
527 | } | |
528 | Remote(url) | |
529 | }) | |
530 | .next() | |
531 | .unwrap_or(Unknown) // Well, at least we tried. | |
e74abb32 XL |
532 | } |
533 | ||
534 | /// Builds the search index from the collected metadata | |
535 | fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { | |
74b04a01 | 536 | let mut defid_to_pathid = FxHashMap::default(); |
e74abb32 | 537 | let mut crate_items = Vec::with_capacity(cache.search_index.len()); |
60c5eb7d | 538 | let mut crate_paths = vec![]; |
e74abb32 | 539 | |
f9f354fc XL |
540 | let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } = |
541 | *cache; | |
e74abb32 XL |
542 | |
543 | // Attach all orphan items to the type's definition if the type | |
544 | // has since been learned. | |
545 | for &(did, ref item) in orphan_impl_items { | |
546 | if let Some(&(ref fqp, _)) = paths.get(&did) { | |
547 | search_index.push(IndexItem { | |
548 | ty: item.type_(), | |
549 | name: item.name.clone().unwrap(), | |
550 | path: fqp[..fqp.len() - 1].join("::"), | |
551 | desc: shorten(plain_summary_line(item.doc_value())), | |
552 | parent: Some(did), | |
553 | parent_idx: None, | |
554 | search_type: get_index_search_type(&item), | |
555 | }); | |
f9f354fc XL |
556 | for alias in item.attrs.get_doc_aliases() { |
557 | aliases | |
558 | .entry(alias.to_lowercase()) | |
559 | .or_insert(Vec::new()) | |
560 | .push(search_index.len() - 1); | |
561 | } | |
e74abb32 XL |
562 | } |
563 | } | |
564 | ||
74b04a01 | 565 | // Reduce `DefId` in paths into smaller sequential numbers, |
e74abb32 XL |
566 | // and prune the paths that do not appear in the index. |
567 | let mut lastpath = String::new(); | |
568 | let mut lastpathid = 0usize; | |
569 | ||
570 | for item in search_index { | |
ba9703b0 | 571 | item.parent_idx = item.parent.and_then(|defid| { |
74b04a01 | 572 | if defid_to_pathid.contains_key(&defid) { |
ba9703b0 | 573 | defid_to_pathid.get(&defid).copied() |
e74abb32 XL |
574 | } else { |
575 | let pathid = lastpathid; | |
74b04a01 | 576 | defid_to_pathid.insert(defid, pathid); |
e74abb32 XL |
577 | lastpathid += 1; |
578 | ||
ba9703b0 XL |
579 | if let Some(&(ref fqp, short)) = paths.get(&defid) { |
580 | crate_paths.push((short, fqp.last().unwrap().clone())); | |
581 | Some(pathid) | |
582 | } else { | |
583 | None | |
584 | } | |
e74abb32 XL |
585 | } |
586 | }); | |
587 | ||
588 | // Omit the parent path if it is same to that of the prior item. | |
589 | if lastpath == item.path { | |
590 | item.path.clear(); | |
591 | } else { | |
592 | lastpath = item.path.clone(); | |
593 | } | |
60c5eb7d | 594 | crate_items.push(&*item); |
e74abb32 XL |
595 | } |
596 | ||
dfeec247 XL |
597 | let crate_doc = krate |
598 | .module | |
599 | .as_ref() | |
600 | .map(|module| shorten(plain_summary_line(module.doc_value()))) | |
601 | .unwrap_or(String::new()); | |
e74abb32 | 602 | |
60c5eb7d XL |
603 | #[derive(Serialize)] |
604 | struct CrateData<'a> { | |
605 | doc: String, | |
606 | #[serde(rename = "i")] | |
607 | items: Vec<&'a IndexItem>, | |
608 | #[serde(rename = "p")] | |
609 | paths: Vec<(ItemType, String)>, | |
f9f354fc XL |
610 | // The String is alias name and the vec is the list of the elements with this alias. |
611 | // | |
612 | // To be noted: the `usize` elements are indexes to `items`. | |
613 | #[serde(rename = "a")] | |
614 | #[serde(skip_serializing_if = "BTreeMap::is_empty")] | |
615 | aliases: &'a BTreeMap<String, Vec<usize>>, | |
60c5eb7d | 616 | } |
e74abb32 XL |
617 | |
618 | // Collect the index into a string | |
60c5eb7d | 619 | format!( |
ba9703b0 | 620 | r#""{}":{}"#, |
60c5eb7d XL |
621 | krate.name, |
622 | serde_json::to_string(&CrateData { | |
623 | doc: crate_doc, | |
624 | items: crate_items, | |
625 | paths: crate_paths, | |
f9f354fc | 626 | aliases, |
60c5eb7d | 627 | }) |
dfeec247 | 628 | .expect("failed serde conversion") |
ba9703b0 XL |
629 | // All these `replace` calls are because we have to go through JS string for JSON content. |
630 | .replace(r"\", r"\\") | |
631 | .replace("'", r"\'") | |
632 | // We need to escape double quotes for the JSON. | |
633 | .replace("\\\"", "\\\\\"") | |
60c5eb7d | 634 | ) |
e74abb32 XL |
635 | } |
636 | ||
637 | fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> { | |
638 | let (all_types, ret_types) = match item.inner { | |
639 | clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types), | |
640 | clean::MethodItem(ref m) => (&m.all_types, &m.ret_types), | |
641 | clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types), | |
642 | _ => return None, | |
643 | }; | |
644 | ||
ba9703b0 XL |
645 | let inputs = all_types |
646 | .iter() | |
647 | .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) | |
648 | .filter(|a| a.ty.name.is_some()) | |
649 | .collect(); | |
dfeec247 XL |
650 | let output = ret_types |
651 | .iter() | |
ba9703b0 XL |
652 | .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) |
653 | .filter(|a| a.ty.name.is_some()) | |
dfeec247 XL |
654 | .collect::<Vec<_>>(); |
655 | let output = if output.is_empty() { None } else { Some(output) }; | |
e74abb32 XL |
656 | |
657 | Some(IndexItemFunctionType { inputs, output }) | |
658 | } | |
659 | ||
ba9703b0 XL |
660 | fn get_index_type(clean_type: &clean::Type) -> RenderType { |
661 | RenderType { | |
662 | ty: clean_type.def_id(), | |
663 | idx: None, | |
e74abb32 XL |
664 | name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()), |
665 | generics: get_generics(clean_type), | |
ba9703b0 | 666 | } |
e74abb32 XL |
667 | } |
668 | ||
669 | fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> { | |
670 | match *clean_type { | |
671 | clean::ResolvedPath { ref path, .. } => { | |
672 | let segments = &path.segments; | |
74b04a01 | 673 | let path_segment = segments.iter().last().unwrap_or_else(|| panic!( |
e74abb32 XL |
674 | "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path", |
675 | clean_type, accept_generic | |
676 | )); | |
677 | Some(path_segment.name.clone()) | |
678 | } | |
679 | clean::Generic(ref s) if accept_generic => Some(s.clone()), | |
680 | clean::Primitive(ref p) => Some(format!("{:?}", p)), | |
681 | clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic), | |
682 | // FIXME: add all from clean::Type. | |
dfeec247 | 683 | _ => None, |
e74abb32 XL |
684 | } |
685 | } | |
686 | ||
ba9703b0 | 687 | fn get_generics(clean_type: &clean::Type) -> Option<Vec<Generic>> { |
dfeec247 XL |
688 | clean_type.generics().and_then(|types| { |
689 | let r = types | |
690 | .iter() | |
ba9703b0 XL |
691 | .filter_map(|t| { |
692 | get_index_type_name(t, false).map(|name| Generic { | |
693 | name: name.to_ascii_lowercase(), | |
694 | defid: t.def_id(), | |
695 | idx: None, | |
696 | }) | |
697 | }) | |
dfeec247 XL |
698 | .collect::<Vec<_>>(); |
699 | if r.is_empty() { None } else { Some(r) } | |
700 | }) | |
e74abb32 | 701 | } |