1 use std
::collections
::BTreeMap
;
3 use std
::path
::{Path, PathBuf}
;
5 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
6 use rustc_hir
::def_id
::{CrateNum, DefId, CRATE_DEF_INDEX}
;
7 use rustc_middle
::middle
::privacy
::AccessLevels
;
8 use rustc_span
::source_map
::FileName
;
9 use rustc_span
::Symbol
;
11 use crate::clean
::{self, GetDefId}
;
12 use crate::config
::RenderInfo
;
13 use crate::fold
::DocFolder
;
14 use crate::formats
::item_type
::ItemType
;
15 use crate::formats
::Impl
;
16 use crate::html
::markdown
::short_markdown_summary
;
17 use crate::html
::render
::cache
::{extern_location, get_index_search_type, ExternalLocation}
;
18 use crate::html
::render
::IndexItem
;
20 /// This cache is used to store information about the [`clean::Crate`] being
21 /// rendered in order to provide more useful documentation. This contains
22 /// information like all implementors of a trait, all traits a type implements,
23 /// documentation for all known traits, etc.
25 /// This structure purposefully does not implement `Clone` because it's intended
26 /// to be a fairly large and expensive structure to clone. Instead this adheres
27 /// to `Send` so it may be stored in a `Arc` instance and shared among the various
28 /// rendering threads.
31 /// Maps a type ID to all known implementations for that type. This is only
32 /// recognized for intra-crate `ResolvedPath` types, and is used to print
33 /// out extra documentation on the page of an enum/struct.
35 /// The values of the map are a list of implementations and documentation
36 /// found on that implementation.
37 crate impls
: FxHashMap
<DefId
, Vec
<Impl
>>,
39 /// Maintains a mapping of local crate `DefId`s to the fully qualified name
40 /// and "short type description" of that node. This is used when generating
41 /// URLs when a type is being linked to. External paths are not located in
42 /// this map because the `External` type itself has all the information
44 crate paths
: FxHashMap
<DefId
, (Vec
<String
>, ItemType
)>,
46 /// Similar to `paths`, but only holds external paths. This is only used for
47 /// generating explicit hyperlinks to other crates.
48 crate external_paths
: FxHashMap
<DefId
, (Vec
<String
>, ItemType
)>,
50 /// Maps local `DefId`s of exported types to fully qualified paths.
51 /// Unlike 'paths', this mapping ignores any renames that occur
52 /// due to 'use' statements.
54 /// This map is used when writing out the special 'implementors'
55 /// javascript file. By using the exact path that the type
56 /// is declared with, we ensure that each path will be identical
57 /// to the path used if the corresponding type is inlined. By
58 /// doing this, we can detect duplicate impls on a trait page, and only display
59 /// the impl for the inlined type.
60 crate exact_paths
: FxHashMap
<DefId
, Vec
<String
>>,
62 /// This map contains information about all known traits of this crate.
63 /// Implementations of a crate should inherit the documentation of the
64 /// parent trait if no extra documentation is specified, and default methods
65 /// should show up in documentation about trait implementations.
66 crate traits
: FxHashMap
<DefId
, clean
::Trait
>,
68 /// When rendering traits, it's often useful to be able to list all
69 /// implementors of the trait, and this mapping is exactly, that: a mapping
70 /// of trait ids to the list of known implementors of the trait
71 crate implementors
: FxHashMap
<DefId
, Vec
<Impl
>>,
73 /// Cache of where external crate documentation can be found.
74 crate extern_locations
: FxHashMap
<CrateNum
, (Symbol
, PathBuf
, ExternalLocation
)>,
76 /// Cache of where documentation for primitives can be found.
77 crate primitive_locations
: FxHashMap
<clean
::PrimitiveType
, DefId
>,
79 // Note that external items for which `doc(hidden)` applies to are shown as
80 // non-reachable while local items aren't. This is because we're reusing
81 // the access levels from the privacy check pass.
82 crate access_levels
: AccessLevels
<DefId
>,
84 /// The version of the crate being documented, if given from the `--crate-version` flag.
85 crate crate_version
: Option
<String
>,
87 /// Whether to document private items.
88 /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
89 crate document_private
: bool
,
91 // Private fields only used when initially crawling a crate to build a cache
93 parent_stack
: Vec
<DefId
>,
94 parent_is_trait_impl
: bool
,
96 masked_crates
: FxHashSet
<CrateNum
>,
98 crate search_index
: Vec
<IndexItem
>,
99 crate deref_trait_did
: Option
<DefId
>,
100 crate deref_mut_trait_did
: Option
<DefId
>,
101 crate owned_box_did
: Option
<DefId
>,
103 // In rare case where a structure is defined in one module but implemented
104 // in another, if the implementing module is parsed before defining module,
105 // then the fully qualified name of the structure isn't presented in `paths`
106 // yet when its implementation methods are being indexed. Caches such methods
107 // and their parent id here and indexes them at the end of crate parsing.
108 crate orphan_impl_items
: Vec
<(DefId
, clean
::Item
)>,
110 // Similarly to `orphan_impl_items`, sometimes trait impls are picked up
111 // even though the trait itself is not exported. This can happen if a trait
112 // was defined in function/expression scope, since the impl will be picked
113 // up by `collect-trait-impls` but the trait won't be scraped out in the HIR
114 // crawl. In order to prevent crashes when looking for spotlight traits or
115 // when gathering trait documentation on a type, hold impls here while
116 // folding and add them to the cache later on if we find the trait.
117 orphan_trait_impls
: Vec
<(DefId
, FxHashSet
<DefId
>, Impl
)>,
119 /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
120 /// we need the alias element to have an array of items.
121 crate aliases
: BTreeMap
<String
, Vec
<usize>>,
126 render_info
: RenderInfo
,
127 document_private
: bool
,
128 extern_html_root_urls
: &BTreeMap
<String
, String
>,
130 mut krate
: clean
::Crate
,
131 ) -> (clean
::Crate
, Cache
) {
132 // Crawl the crate to build various caches used for the output
145 external_paths
.into_iter().map(|(k
, (v
, t
))| (k
, (v
, ItemType
::from(t
)))).collect();
147 let mut cache
= Cache
{
150 parent_is_trait_impl
: false,
153 crate_version
: krate
.version
.take(),
155 traits
: krate
.external_traits
.replace(Default
::default()),
159 masked_crates
: mem
::take(&mut krate
.masked_crates
),
163 // Cache where all our extern crates are located
164 // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
165 for &(n
, ref e
) in &krate
.externs
{
166 let src_root
= match e
.src
{
167 FileName
::Real(ref p
) => match p
.local_path().parent() {
168 Some(p
) => p
.to_path_buf(),
169 None
=> PathBuf
::new(),
173 let extern_url
= extern_html_root_urls
.get(&*e
.name
.as_str()).map(|u
| &**u
);
176 .insert(n
, (e
.name
, src_root
, extern_location(e
, extern_url
, &dst
)));
178 let did
= DefId { krate: n, index: CRATE_DEF_INDEX }
;
179 cache
.external_paths
.insert(did
, (vec
![e
.name
.to_string()], ItemType
::Module
));
182 // Cache where all known primitives have their documentation located.
184 // Favor linking to as local extern as possible, so iterate all crates in
185 // reverse topological order.
186 for &(_
, ref e
) in krate
.externs
.iter().rev() {
187 for &(def_id
, prim
) in &e
.primitives
{
188 cache
.primitive_locations
.insert(prim
, def_id
);
191 for &(def_id
, prim
) in &krate
.primitives
{
192 cache
.primitive_locations
.insert(prim
, def_id
);
195 cache
.stack
.push(krate
.name
.to_string());
197 krate
= cache
.fold_crate(krate
);
199 for (trait_did
, dids
, impl_
) in cache
.orphan_trait_impls
.drain(..) {
200 if cache
.traits
.contains_key(&trait_did
) {
202 cache
.impls
.entry(did
).or_default().push(impl_
.clone());
211 impl DocFolder
for Cache
{
212 fn fold_item(&mut self, item
: clean
::Item
) -> Option
<clean
::Item
> {
213 if item
.def_id
.is_local() {
214 debug
!("folding {} \"{:?}\", id {:?}", item
.type_(), item
.name
, item
.def_id
);
217 // If this is a stripped module,
218 // we don't want it or its children in the search index.
219 let orig_stripped_mod
= match *item
.kind
{
220 clean
::StrippedItem(box clean
::ModuleItem(..)) => {
221 mem
::replace(&mut self.stripped_mod
, true)
223 _
=> self.stripped_mod
,
226 // If the impl is from a masked crate or references something from a
227 // masked crate then remove it completely.
228 if let clean
::ImplItem(ref i
) = *item
.kind
{
229 if self.masked_crates
.contains(&item
.def_id
.krate
)
230 || i
.trait_
.def_id().map_or(false, |d
| self.masked_crates
.contains(&d
.krate
))
231 || i
.for_
.def_id().map_or(false, |d
| self.masked_crates
.contains(&d
.krate
))
237 // Propagate a trait method's documentation to all implementors of the
239 if let clean
::TraitItem(ref t
) = *item
.kind
{
240 self.traits
.entry(item
.def_id
).or_insert_with(|| t
.clone());
243 // Collect all the implementors of traits.
244 if let clean
::ImplItem(ref i
) = *item
.kind
{
245 if let Some(did
) = i
.trait_
.def_id() {
246 if i
.blanket_impl
.is_none() {
250 .push(Impl { impl_item: item.clone() }
);
255 // Index this method for searching later on.
256 if let Some(ref s
) = item
.name
{
257 let (parent
, is_inherent_impl_item
) = match *item
.kind
{
258 clean
::StrippedItem(..) => ((None
, None
), false),
259 clean
::AssocConstItem(..) | clean
::TypedefItem(_
, true)
260 if self.parent_is_trait_impl
=>
262 // skip associated items in trait impls
263 ((None
, None
), false)
265 clean
::AssocTypeItem(..)
266 | clean
::TyMethodItem(..)
267 | clean
::StructFieldItem(..)
268 | clean
::VariantItem(..) => (
270 Some(*self.parent_stack
.last().expect("parent_stack is empty")),
271 Some(&self.stack
[..self.stack
.len() - 1]),
275 clean
::MethodItem(..) | clean
::AssocConstItem(..) => {
276 if self.parent_stack
.is_empty() {
277 ((None
, None
), false)
279 let last
= self.parent_stack
.last().expect("parent_stack is empty 2");
281 let path
= match self.paths
.get(&did
) {
282 // The current stack not necessarily has correlation
283 // for where the type was defined. On the other
284 // hand, `paths` always has the right
285 // information if present.
292 )) => Some(&fqp
[..fqp
.len() - 1]),
293 Some(..) => Some(&*self.stack
),
296 ((Some(*last
), path
), true)
299 _
=> ((None
, Some(&*self.stack
)), false),
303 (parent
, Some(path
)) if is_inherent_impl_item
|| !self.stripped_mod
=> {
304 debug_assert
!(!item
.is_stripped());
306 // A crate has a module at its root, containing all items,
307 // which should not be indexed. The crate-item itself is
308 // inserted later on when serializing the search-index.
309 if item
.def_id
.index
!= CRATE_DEF_INDEX
{
310 self.search_index
.push(IndexItem
{
313 path
: path
.join("::"),
316 .map_or_else(String
::new
, |x
| short_markdown_summary(&x
.as_str())),
319 search_type
: get_index_search_type(&item
, None
),
322 for alias
in item
.attrs
.get_doc_aliases() {
324 .entry(alias
.to_lowercase())
325 .or_insert(Vec
::new())
326 .push(self.search_index
.len() - 1);
330 (Some(parent
), None
) if is_inherent_impl_item
=> {
331 // We have a parent, but we don't know where they're
332 // defined yet. Wait for later to index this item.
333 self.orphan_impl_items
.push((parent
, item
.clone()));
339 // Keep track of the fully qualified path for this item.
340 let pushed
= match item
.name
{
341 Some(n
) if !n
.is_empty() => {
342 self.stack
.push(n
.to_string());
349 clean
::StructItem(..)
350 | clean
::EnumItem(..)
351 | clean
::TypedefItem(..)
352 | clean
::TraitItem(..)
353 | clean
::FunctionItem(..)
354 | clean
::ModuleItem(..)
355 | clean
::ForeignFunctionItem(..)
356 | clean
::ForeignStaticItem(..)
357 | clean
::ConstantItem(..)
358 | clean
::StaticItem(..)
359 | clean
::UnionItem(..)
360 | clean
::ForeignTypeItem
361 | clean
::MacroItem(..)
362 | clean
::ProcMacroItem(..)
363 | clean
::VariantItem(..)
364 if !self.stripped_mod
=>
366 // Re-exported items mean that the same id can show up twice
367 // in the rustdoc ast that we're looking at. We know,
368 // however, that a re-exported item doesn't show up in the
369 // `public_items` map, so we can skip inserting into the
370 // paths map if there was already an entry present and we're
371 // not a public item.
372 if !self.paths
.contains_key(&item
.def_id
)
373 || self.access_levels
.is_public(item
.def_id
)
375 self.paths
.insert(item
.def_id
, (self.stack
.clone(), item
.type_()));
378 clean
::PrimitiveItem(..) => {
379 self.paths
.insert(item
.def_id
, (self.stack
.clone(), item
.type_()));
385 // Maintain the parent stack
386 let orig_parent_is_trait_impl
= self.parent_is_trait_impl
;
387 let parent_pushed
= match *item
.kind
{
389 | clean
::EnumItem(..)
390 | clean
::ForeignTypeItem
391 | clean
::StructItem(..)
392 | clean
::UnionItem(..)
393 | clean
::VariantItem(..) => {
394 self.parent_stack
.push(item
.def_id
);
395 self.parent_is_trait_impl
= false;
398 clean
::ImplItem(ref i
) => {
399 self.parent_is_trait_impl
= i
.trait_
.is_some();
401 clean
::ResolvedPath { did, .. }
=> {
402 self.parent_stack
.push(did
);
408 .and_then(|t
| self.primitive_locations
.get(&t
).cloned());
411 self.parent_stack
.push(did
);
422 // Once we've recursively found all the generics, hoard off all the
423 // implementations elsewhere.
424 let item
= self.fold_item_recur(item
);
425 let ret
= if let clean
::Item { kind: box clean::ImplItem(ref i), .. }
= item
{
426 // Figure out the id of this impl. This may map to a
427 // primitive rather than always to a struct/enum.
428 // Note: matching twice to restrict the lifetime of the `i` borrow.
429 let mut dids
= FxHashSet
::default();
431 clean
::ResolvedPath { did, .. }
432 | clean
::BorrowedRef { type_: box clean::ResolvedPath { did, .. }
, .. } => {
437 t
.primitive_type().and_then(|t
| self.primitive_locations
.get(&t
).cloned());
439 if let Some(did
) = did
{
445 if let Some(generics
) = i
.trait_
.as_ref().and_then(|t
| t
.generics()) {
446 for bound
in generics
{
447 if let Some(did
) = bound
.def_id() {
452 let impl_item
= Impl { impl_item: item }
;
453 if impl_item
.trait_did().map_or(true, |d
| self.traits
.contains_key(&d
)) {
455 self.impls
.entry(did
).or_insert(vec
![]).push(impl_item
.clone());
458 let trait_did
= impl_item
.trait_did().expect("no trait did");
459 self.orphan_trait_impls
.push((trait_did
, dids
, impl_item
));
467 self.stack
.pop().expect("stack already empty");
470 self.parent_stack
.pop().expect("parent stack already empty");
472 self.stripped_mod
= orig_stripped_mod
;
473 self.parent_is_trait_impl
= orig_parent_is_trait_impl
;