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