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
;
10 use std
::path
::{Path, PathBuf}
;
14 use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}
;
15 use super::{Generic, RenderInfo, RenderType, TypeWithKind}
;
17 /// Indicates where an external crate can be found.
18 pub enum ExternalLocation
{
19 /// Remote URL root of the external crate
21 /// This external crate can be found in the local doc/ folder
23 /// The external crate could not be found.
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.
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.
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.
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
>>,
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
51 pub paths
: FxHashMap
<DefId
, (Vec
<String
>, ItemType
)>,
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
)>,
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.
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
>>,
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
>,
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
>>,
80 /// Cache of where external crate documentation can be found.
81 pub extern_locations
: FxHashMap
<CrateNum
, (String
, PathBuf
, ExternalLocation
)>,
83 /// Cache of where documentation for primitives can be found.
84 pub primitive_locations
: FxHashMap
<clean
::PrimitiveType
, DefId
>,
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
>,
91 /// The version of the crate being documented, if given from the `--crate-version` flag.
92 pub crate_version
: Option
<String
>,
94 // Private fields only used when initially crawling a crate to build a cache
96 parent_stack
: Vec
<DefId
>,
97 parent_is_trait_impl
: bool
,
98 search_index
: Vec
<IndexItem
>,
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
>,
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
)>,
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
)>,
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
>>,
128 renderinfo
: RenderInfo
,
129 extern_html_root_urls
: &BTreeMap
<String
, String
>,
131 mut krate
: clean
::Crate
,
132 ) -> (clean
::Crate
, String
, Cache
) {
133 // Crawl the crate to build various caches used for the output
146 external_paths
.into_iter().map(|(k
, (v
, t
))| (k
, (v
, ItemType
::from(t
)))).collect();
148 let mut cache
= Cache
{
149 impls
: Default
::default(),
152 paths
: Default
::default(),
153 implementors
: Default
::default(),
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(),
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()),
169 masked_crates
: mem
::take(&mut krate
.masked_crates
),
170 aliases
: Default
::default(),
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(),
182 let extern_url
= extern_html_root_urls
.get(&e
.name
).map(|u
| &**u
);
185 .insert(n
, (e
.name
.clone(), src_root
, extern_location(e
, extern_url
, &dst
)));
187 let did
= DefId { krate: n, index: CRATE_DEF_INDEX }
;
188 cache
.external_paths
.insert(did
, (vec
![e
.name
.to_string()], ItemType
::Module
));
191 // Cache where all known primitives have their documentation located.
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
);
200 for &(def_id
, prim
, _
) in &krate
.primitives
{
201 cache
.primitive_locations
.insert(prim
, def_id
);
204 cache
.stack
.push(krate
.name
.clone());
205 krate
= cache
.fold_crate(krate
);
207 for (trait_did
, dids
, impl_
) in cache
.orphan_trait_impls
.drain(..) {
208 if cache
.traits
.contains_key(&trait_did
) {
210 cache
.impls
.entry(did
).or_insert(vec
![]).push(impl_
.clone());
215 // Build our search index
216 let index
= build_index(&krate
, &mut cache
);
218 (krate
, index
, cache
)
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
);
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)
234 _
=> self.stripped_mod
,
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
))
248 // Propagate a trait method's documentation to all implementors of the
250 if let clean
::TraitItem(ref t
) = item
.inner
{
251 self.traits
.entry(item
.def_id
).or_insert_with(|| t
.clone());
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() {
261 .push(Impl { impl_item: item.clone() }
);
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
=>
273 // skip associated items in trait impls
274 ((None
, None
), false)
276 clean
::AssocTypeItem(..)
277 | clean
::TyMethodItem(..)
278 | clean
::StructFieldItem(..)
279 | clean
::VariantItem(..) => (
281 Some(*self.parent_stack
.last().expect("parent_stack is empty")),
282 Some(&self.stack
[..self.stack
.len() - 1]),
286 clean
::MethodItem(..) | clean
::AssocConstItem(..) => {
287 if self.parent_stack
.is_empty() {
288 ((None
, None
), false)
290 let last
= self.parent_stack
.last().expect("parent_stack is empty 2");
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.
303 )) => Some(&fqp
[..fqp
.len() - 1]),
304 Some(..) => Some(&*self.stack
),
307 ((Some(*last
), path
), true)
310 _
=> ((None
, Some(&*self.stack
)), false),
314 (parent
, Some(path
)) if is_inherent_impl_item
|| (!self.stripped_mod
) => {
315 debug_assert
!(!item
.is_stripped());
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
{
324 path
: path
.join("::"),
325 desc
: shorten(plain_summary_line(item
.doc_value())),
328 search_type
: get_index_search_type(&item
),
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()));
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());
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
=>
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
)
377 self.paths
.insert(item
.def_id
, (self.stack
.clone(), item
.type_()));
379 self.add_aliases(&item
);
382 clean
::PrimitiveItem(..) => {
383 self.add_aliases(&item
);
384 self.paths
.insert(item
.def_id
, (self.stack
.clone(), item
.type_()));
390 // Maintain the parent stack
391 let orig_parent_is_trait_impl
= self.parent_is_trait_impl
;
392 let parent_pushed
= match item
.inner
{
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;
403 clean
::ImplItem(ref i
) => {
404 self.parent_is_trait_impl
= i
.trait_
.is_some();
406 clean
::ResolvedPath { did, .. }
=> {
407 self.parent_stack
.push(did
);
413 .and_then(|t
| self.primitive_locations
.get(&t
).cloned());
416 self.parent_stack
.push(did
);
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
{
437 clean
::ResolvedPath { did, .. }
438 | clean
::BorrowedRef
{
439 type_
: box clean
::ResolvedPath { did, .. }
, ..
446 .and_then(|t
| self.primitive_locations
.get(&t
).cloned());
448 if let Some(did
) = did
{
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() {
464 let impl_item
= Impl { impl_item: item }
;
465 if impl_item
.trait_did().map_or(true, |d
| self.traits
.contains_key(&d
)) {
467 self.impls
.entry(did
).or_insert(vec
![]).push(impl_item
.clone());
470 let trait_did
= impl_item
.trait_did().expect("no trait did");
471 self.orphan_trait_impls
.push((trait_did
, dids
, impl_item
));
480 self.stack
.pop().expect("stack already empty");
483 self.parent_stack
.pop().expect("parent stack already empty");
485 self.stripped_mod
= orig_stripped_mod
;
486 self.parent_is_trait_impl
= orig_parent_is_trait_impl
;
492 fn add_aliases(&mut self, item
: &clean
::Item
) {
493 if item
.def_id
.index
== CRATE_DEF_INDEX
{
496 if let Some(ref item_name
) = item
.name
{
500 .map(|p
| p
.0[..p
.0.len() - 1].join("::"))
501 .unwrap_or("std".to_owned());
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
<_
>>()
511 self.aliases
.entry(alias
).or_insert(Vec
::with_capacity(1)).push(IndexItem
{
513 name
: item_name
.to_string(),
515 desc
: shorten(plain_summary_line(item
.doc_value())),
518 search_type
: get_index_search_type(&item
),
525 /// Attempts to find where an external crate is located, given that we're
526 /// rendering in to the specified source destination.
528 e
: &clean
::ExternalCrate
,
529 extern_url
: Option
<&str>,
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() {
539 if let Some(url
) = extern_url
{
540 let mut url
= url
.to_string();
541 if !url
.ends_with('
/'
) {
547 // Failing that, see if there's an attribute specifying where to find this
551 .filter(|a
| a
.check_name(sym
::html_root_url
))
552 .filter_map(|a
| a
.value_str())
554 let mut url
= url
.to_string();
555 if !url
.ends_with('
/'
) {
561 .unwrap_or(Unknown
) // Well, at least we tried.
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
![];
570 let Cache { ref mut search_index, ref orphan_impl_items, ref paths, .. }
= *cache
;
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
{
578 name
: item
.name
.clone().unwrap(),
579 path
: fqp
[..fqp
.len() - 1].join("::"),
580 desc
: shorten(plain_summary_line(item
.doc_value())),
583 search_type
: get_index_search_type(&item
),
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
;
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()
598 let pathid
= lastpathid
;
599 defid_to_pathid
.insert(defid
, pathid
);
602 if let Some(&(ref fqp
, short
)) = paths
.get(&defid
) {
603 crate_paths
.push((short
, fqp
.last().unwrap().clone()));
611 // Omit the parent path if it is same to that of the prior item.
612 if lastpath
== item
.path
{
615 lastpath
= item
.path
.clone();
617 crate_items
.push(&*item
);
620 let crate_doc
= krate
623 .map(|module
| shorten(plain_summary_line(module
.doc_value())))
624 .unwrap_or(String
::new());
627 struct CrateData
<'a
> {
629 #[serde(rename = "i")]
630 items
: Vec
<&'a IndexItem
>,
631 #[serde(rename = "p")]
632 paths
: Vec
<(ItemType
, String
)>,
635 // Collect the index into a string
639 serde_json
::to_string(&CrateData
{
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
"\\")
648 // We need to escape double quotes for the JSON.
649 .replace("\\\"", "\\\\\"")
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
),
661 let inputs
= all_types
663 .map(|(ty
, kind
)| TypeWithKind
::from((get_index_type(&ty
), *kind
)))
664 .filter(|a
| a
.ty
.name
.is_some())
666 let output
= ret_types
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) }
;
673 Some(IndexItemFunctionType { inputs, output }
)
676 fn get_index_type(clean_type
: &clean
::Type
) -> RenderType
{
678 ty
: clean_type
.def_id(),
680 name
: get_index_type_name(clean_type
, true).map(|s
| s
.to_ascii_lowercase()),
681 generics
: get_generics(clean_type
),
685 fn get_index_type_name(clean_type
: &clean
::Type
, accept_generic
: bool
) -> Option
<String
> {
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
693 Some(path_segment
.name
.clone())
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.
703 fn get_generics(clean_type
: &clean
::Type
) -> Option
<Vec
<Generic
>> {
704 clean_type
.generics().and_then(|types
| {
708 get_index_type_name(t
, false).map(|name
| Generic
{
709 name
: name
.to_ascii_lowercase(),
714 .collect
::<Vec
<_
>>();
715 if r
.is_empty() { None }
else { Some(r) }