]> git.proxmox.com Git - rustc.git/blob - src/librustdoc/html/render/cache.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / src / librustdoc / html / render / cache.rs
1 use crate::clean::{self, AttributesExt, GetDefId};
2 use crate::fold::DocFolder;
3 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4 use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
5 use rustc_middle::middle::privacy::AccessLevels;
6 use rustc_span::source_map::FileName;
7 use rustc_span::symbol::sym;
8 use std::collections::BTreeMap;
9 use std::mem;
10 use std::path::{Path, PathBuf};
11
12 use serde::Serialize;
13
14 use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType};
15 use super::{Generic, RenderInfo, RenderType, TypeWithKind};
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
46 /// Maintains a mapping of local crate `DefId`s to the fully qualified name
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
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.
123 pub(super) aliases: FxHashMap<String, Vec<IndexItem>>,
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,
142 ..
143 } = renderinfo;
144
145 let external_paths =
146 external_paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from(t)))).collect();
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 {
176 FileName::Real(ref p) => match p.local_path().parent() {
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);
183 cache
184 .extern_locations
185 .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst)));
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 {
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 {
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() {
258 self.implementors
259 .entry(did)
260 .or_default()
261 .push(Impl { impl_item: item.clone() });
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),
270 clean::AssocConstItem(..) | clean::TypedefItem(_, true)
271 if self.parent_is_trait_impl =>
272 {
273 // skip associated items in trait impls
274 ((None, None), false)
275 }
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 ),
286 clean::MethodItem(..) | clean::AssocConstItem(..) => {
287 if self.parent_stack.is_empty() {
288 ((None, None), false)
289 } else {
290 let last = self.parent_stack.last().expect("parent_stack is empty 2");
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.
297 Some(&(
298 ref fqp,
299 ItemType::Trait
300 | ItemType::Struct
301 | ItemType::Union
302 | ItemType::Enum,
303 )) => Some(&fqp[..fqp.len() - 1]),
304 Some(..) => Some(&*self.stack),
305 None => None,
306 };
307 ((Some(*last), path), true)
308 }
309 }
310 _ => ((None, Some(&*self.stack)), false),
311 };
312
313 match parent {
314 (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
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 });
330 }
331 }
332 (Some(parent), None) if is_inherent_impl_item => {
333 // We have a parent, but we don't know where they're
334 // defined yet. Wait for later to index this item.
335 self.orphan_impl_items.push((parent, item.clone()));
336 }
337 _ => {}
338 }
339 }
340
341 // Keep track of the fully qualified path for this item.
342 let pushed = match item.name {
343 Some(ref n) if !n.is_empty() => {
344 self.stack.push(n.to_string());
345 true
346 }
347 _ => false,
348 };
349
350 match item.inner {
351 clean::StructItem(..)
352 | clean::EnumItem(..)
353 | clean::TypedefItem(..)
354 | clean::TraitItem(..)
355 | clean::FunctionItem(..)
356 | clean::ModuleItem(..)
357 | clean::ForeignFunctionItem(..)
358 | clean::ForeignStaticItem(..)
359 | clean::ConstantItem(..)
360 | clean::StaticItem(..)
361 | clean::UnionItem(..)
362 | clean::ForeignTypeItem
363 | clean::MacroItem(..)
364 | clean::ProcMacroItem(..)
365 | clean::VariantItem(..)
366 if !self.stripped_mod =>
367 {
368 // Re-exported items mean that the same id can show up twice
369 // in the rustdoc ast that we're looking at. We know,
370 // however, that a re-exported item doesn't show up in the
371 // `public_items` map, so we can skip inserting into the
372 // paths map if there was already an entry present and we're
373 // not a public item.
374 if !self.paths.contains_key(&item.def_id)
375 || self.access_levels.is_public(item.def_id)
376 {
377 self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
378 }
379 self.add_aliases(&item);
380 }
381
382 clean::PrimitiveItem(..) => {
383 self.add_aliases(&item);
384 self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
385 }
386
387 _ => {}
388 }
389
390 // Maintain the parent stack
391 let orig_parent_is_trait_impl = self.parent_is_trait_impl;
392 let parent_pushed = match item.inner {
393 clean::TraitItem(..)
394 | clean::EnumItem(..)
395 | clean::ForeignTypeItem
396 | clean::StructItem(..)
397 | clean::UnionItem(..)
398 | clean::VariantItem(..) => {
399 self.parent_stack.push(item.def_id);
400 self.parent_is_trait_impl = false;
401 true
402 }
403 clean::ImplItem(ref i) => {
404 self.parent_is_trait_impl = i.trait_.is_some();
405 match i.for_ {
406 clean::ResolvedPath { did, .. } => {
407 self.parent_stack.push(did);
408 true
409 }
410 ref t => {
411 let prim_did = t
412 .primitive_type()
413 .and_then(|t| self.primitive_locations.get(&t).cloned());
414 match prim_did {
415 Some(did) => {
416 self.parent_stack.push(did);
417 true
418 }
419 None => false,
420 }
421 }
422 }
423 }
424 _ => false,
425 };
426
427 // Once we've recursively found all the generics, hoard off all the
428 // implementations elsewhere.
429 let ret = self.fold_item_recur(item).and_then(|item| {
430 if let clean::Item { inner: clean::ImplItem(_), .. } = item {
431 // Figure out the id of this impl. This may map to a
432 // primitive rather than always to a struct/enum.
433 // Note: matching twice to restrict the lifetime of the `i` borrow.
434 let mut dids = FxHashSet::default();
435 if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
436 match i.for_ {
437 clean::ResolvedPath { did, .. }
438 | clean::BorrowedRef {
439 type_: box clean::ResolvedPath { did, .. }, ..
440 } => {
441 dids.insert(did);
442 }
443 ref t => {
444 let did = t
445 .primitive_type()
446 .and_then(|t| self.primitive_locations.get(&t).cloned());
447
448 if let Some(did) = did {
449 dids.insert(did);
450 }
451 }
452 }
453
454 if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
455 for bound in generics {
456 if let Some(did) = bound.def_id() {
457 dids.insert(did);
458 }
459 }
460 }
461 } else {
462 unreachable!()
463 };
464 let impl_item = Impl { impl_item: item };
465 if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) {
466 for did in dids {
467 self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
468 }
469 } else {
470 let trait_did = impl_item.trait_did().expect("no trait did");
471 self.orphan_trait_impls.push((trait_did, dids, impl_item));
472 }
473 None
474 } else {
475 Some(item)
476 }
477 });
478
479 if pushed {
480 self.stack.pop().expect("stack already empty");
481 }
482 if parent_pushed {
483 self.parent_stack.pop().expect("parent stack already empty");
484 }
485 self.stripped_mod = orig_stripped_mod;
486 self.parent_is_trait_impl = orig_parent_is_trait_impl;
487 ret
488 }
489 }
490
491 impl Cache {
492 fn add_aliases(&mut self, item: &clean::Item) {
493 if item.def_id.index == CRATE_DEF_INDEX {
494 return;
495 }
496 if let Some(ref item_name) = item.name {
497 let path = self
498 .paths
499 .get(&item.def_id)
500 .map(|p| p.0[..p.0.len() - 1].join("::"))
501 .unwrap_or("std".to_owned());
502 for alias in item
503 .attrs
504 .lists(sym::doc)
505 .filter(|a| a.check_name(sym::alias))
506 .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", "")))
507 .filter(|v| !v.is_empty())
508 .collect::<FxHashSet<_>>()
509 .into_iter()
510 {
511 self.aliases.entry(alias).or_insert(Vec::with_capacity(1)).push(IndexItem {
512 ty: item.type_(),
513 name: item_name.to_string(),
514 path: path.clone(),
515 desc: shorten(plain_summary_line(item.doc_value())),
516 parent: None,
517 parent_idx: None,
518 search_type: get_index_search_type(&item),
519 });
520 }
521 }
522 }
523 }
524
525 /// Attempts to find where an external crate is located, given that we're
526 /// rendering in to the specified source destination.
527 fn extern_location(
528 e: &clean::ExternalCrate,
529 extern_url: Option<&str>,
530 dst: &Path,
531 ) -> ExternalLocation {
532 use ExternalLocation::*;
533 // See if there's documentation generated into the local directory
534 let local_location = dst.join(&e.name);
535 if local_location.is_dir() {
536 return Local;
537 }
538
539 if let Some(url) = extern_url {
540 let mut url = url.to_string();
541 if !url.ends_with('/') {
542 url.push('/');
543 }
544 return Remote(url);
545 }
546
547 // Failing that, see if there's an attribute specifying where to find this
548 // external crate
549 e.attrs
550 .lists(sym::doc)
551 .filter(|a| a.check_name(sym::html_root_url))
552 .filter_map(|a| a.value_str())
553 .map(|url| {
554 let mut url = url.to_string();
555 if !url.ends_with('/') {
556 url.push('/')
557 }
558 Remote(url)
559 })
560 .next()
561 .unwrap_or(Unknown) // Well, at least we tried.
562 }
563
564 /// Builds the search index from the collected metadata
565 fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
566 let mut defid_to_pathid = FxHashMap::default();
567 let mut crate_items = Vec::with_capacity(cache.search_index.len());
568 let mut crate_paths = vec![];
569
570 let Cache { ref mut search_index, ref orphan_impl_items, ref paths, .. } = *cache;
571
572 // Attach all orphan items to the type's definition if the type
573 // has since been learned.
574 for &(did, ref item) in orphan_impl_items {
575 if let Some(&(ref fqp, _)) = paths.get(&did) {
576 search_index.push(IndexItem {
577 ty: item.type_(),
578 name: item.name.clone().unwrap(),
579 path: fqp[..fqp.len() - 1].join("::"),
580 desc: shorten(plain_summary_line(item.doc_value())),
581 parent: Some(did),
582 parent_idx: None,
583 search_type: get_index_search_type(&item),
584 });
585 }
586 }
587
588 // Reduce `DefId` in paths into smaller sequential numbers,
589 // and prune the paths that do not appear in the index.
590 let mut lastpath = String::new();
591 let mut lastpathid = 0usize;
592
593 for item in search_index {
594 item.parent_idx = item.parent.and_then(|defid| {
595 if defid_to_pathid.contains_key(&defid) {
596 defid_to_pathid.get(&defid).copied()
597 } else {
598 let pathid = lastpathid;
599 defid_to_pathid.insert(defid, pathid);
600 lastpathid += 1;
601
602 if let Some(&(ref fqp, short)) = paths.get(&defid) {
603 crate_paths.push((short, fqp.last().unwrap().clone()));
604 Some(pathid)
605 } else {
606 None
607 }
608 }
609 });
610
611 // Omit the parent path if it is same to that of the prior item.
612 if lastpath == item.path {
613 item.path.clear();
614 } else {
615 lastpath = item.path.clone();
616 }
617 crate_items.push(&*item);
618 }
619
620 let crate_doc = krate
621 .module
622 .as_ref()
623 .map(|module| shorten(plain_summary_line(module.doc_value())))
624 .unwrap_or(String::new());
625
626 #[derive(Serialize)]
627 struct CrateData<'a> {
628 doc: String,
629 #[serde(rename = "i")]
630 items: Vec<&'a IndexItem>,
631 #[serde(rename = "p")]
632 paths: Vec<(ItemType, String)>,
633 }
634
635 // Collect the index into a string
636 format!(
637 r#""{}":{}"#,
638 krate.name,
639 serde_json::to_string(&CrateData {
640 doc: crate_doc,
641 items: crate_items,
642 paths: crate_paths,
643 })
644 .expect("failed serde conversion")
645 // All these `replace` calls are because we have to go through JS string for JSON content.
646 .replace(r"\", r"\\")
647 .replace("'", r"\'")
648 // We need to escape double quotes for the JSON.
649 .replace("\\\"", "\\\\\"")
650 )
651 }
652
653 fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
654 let (all_types, ret_types) = match item.inner {
655 clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
656 clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
657 clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
658 _ => return None,
659 };
660
661 let inputs = all_types
662 .iter()
663 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind)))
664 .filter(|a| a.ty.name.is_some())
665 .collect();
666 let output = ret_types
667 .iter()
668 .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind)))
669 .filter(|a| a.ty.name.is_some())
670 .collect::<Vec<_>>();
671 let output = if output.is_empty() { None } else { Some(output) };
672
673 Some(IndexItemFunctionType { inputs, output })
674 }
675
676 fn get_index_type(clean_type: &clean::Type) -> RenderType {
677 RenderType {
678 ty: clean_type.def_id(),
679 idx: None,
680 name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()),
681 generics: get_generics(clean_type),
682 }
683 }
684
685 fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
686 match *clean_type {
687 clean::ResolvedPath { ref path, .. } => {
688 let segments = &path.segments;
689 let path_segment = segments.iter().last().unwrap_or_else(|| panic!(
690 "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
691 clean_type, accept_generic
692 ));
693 Some(path_segment.name.clone())
694 }
695 clean::Generic(ref s) if accept_generic => Some(s.clone()),
696 clean::Primitive(ref p) => Some(format!("{:?}", p)),
697 clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
698 // FIXME: add all from clean::Type.
699 _ => None,
700 }
701 }
702
703 fn get_generics(clean_type: &clean::Type) -> Option<Vec<Generic>> {
704 clean_type.generics().and_then(|types| {
705 let r = types
706 .iter()
707 .filter_map(|t| {
708 get_index_type_name(t, false).map(|name| Generic {
709 name: name.to_ascii_lowercase(),
710 defid: t.def_id(),
711 idx: None,
712 })
713 })
714 .collect::<Vec<_>>();
715 if r.is_empty() { None } else { Some(r) }
716 })
717 }