]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/formats/cache.rs
rustdoc: Pass DocContext to `Cache::populate`
[rust.git] / src / librustdoc / formats / cache.rs
1 use std::mem;
2
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_middle::ty::TyCtxt;
7 use rustc_span::symbol::sym;
8
9 use crate::clean::{self, ItemId, PrimitiveType};
10 use crate::config::RenderOptions;
11 use crate::core::DocContext;
12 use crate::fold::DocFolder;
13 use crate::formats::item_type::ItemType;
14 use crate::formats::Impl;
15 use crate::html::markdown::short_markdown_summary;
16 use crate::html::render::cache::{get_index_search_type, ExternalLocation};
17 use crate::html::render::IndexItem;
18
19 /// This cache is used to store information about the [`clean::Crate`] being
20 /// rendered in order to provide more useful documentation. This contains
21 /// information like all implementors of a trait, all traits a type implements,
22 /// documentation for all known traits, etc.
23 ///
24 /// This structure purposefully does not implement `Clone` because it's intended
25 /// to be a fairly large and expensive structure to clone. Instead this adheres
26 /// to `Send` so it may be stored in an `Arc` instance and shared among the various
27 /// rendering threads.
28 #[derive(Default)]
29 crate struct Cache {
30     /// Maps a type ID to all known implementations for that type. This is only
31     /// recognized for intra-crate `ResolvedPath` types, and is used to print
32     /// out extra documentation on the page of an enum/struct.
33     ///
34     /// The values of the map are a list of implementations and documentation
35     /// found on that implementation.
36     crate impls: FxHashMap<DefId, Vec<Impl>>,
37
38     /// Maintains a mapping of local crate `DefId`s to the fully qualified name
39     /// and "short type description" of that node. This is used when generating
40     /// URLs when a type is being linked to. External paths are not located in
41     /// this map because the `External` type itself has all the information
42     /// necessary.
43     crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
44
45     /// Similar to `paths`, but only holds external paths. This is only used for
46     /// generating explicit hyperlinks to other crates.
47     crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
48
49     /// Maps local `DefId`s of exported types to fully qualified paths.
50     /// Unlike 'paths', this mapping ignores any renames that occur
51     /// due to 'use' statements.
52     ///
53     /// This map is used when writing out the special 'implementors'
54     /// javascript file. By using the exact path that the type
55     /// is declared with, we ensure that each path will be identical
56     /// to the path used if the corresponding type is inlined. By
57     /// doing this, we can detect duplicate impls on a trait page, and only display
58     /// the impl for the inlined type.
59     crate exact_paths: FxHashMap<DefId, Vec<String>>,
60
61     /// This map contains information about all known traits of this crate.
62     /// Implementations of a crate should inherit the documentation of the
63     /// parent trait if no extra documentation is specified, and default methods
64     /// should show up in documentation about trait implementations.
65     crate traits: FxHashMap<DefId, clean::TraitWithExtraInfo>,
66
67     /// When rendering traits, it's often useful to be able to list all
68     /// implementors of the trait, and this mapping is exactly, that: a mapping
69     /// of trait ids to the list of known implementors of the trait
70     crate implementors: FxHashMap<DefId, Vec<Impl>>,
71
72     /// Cache of where external crate documentation can be found.
73     crate extern_locations: FxHashMap<CrateNum, ExternalLocation>,
74
75     /// Cache of where documentation for primitives can be found.
76     crate primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
77
78     // Note that external items for which `doc(hidden)` applies to are shown as
79     // non-reachable while local items aren't. This is because we're reusing
80     // the access levels from the privacy check pass.
81     crate access_levels: AccessLevels<DefId>,
82
83     /// The version of the crate being documented, if given from the `--crate-version` flag.
84     crate crate_version: Option<String>,
85
86     /// Whether to document private items.
87     /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
88     crate document_private: bool,
89
90     /// Crates marked with [`#[doc(masked)]`][doc_masked].
91     ///
92     /// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html
93     crate masked_crates: FxHashSet<CrateNum>,
94
95     // Private fields only used when initially crawling a crate to build a cache
96     stack: Vec<String>,
97     parent_stack: Vec<DefId>,
98     parent_is_trait_impl: bool,
99     stripped_mod: bool,
100
101     crate search_index: Vec<IndexItem>,
102
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)>,
109
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 notable 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)>,
118
119     /// All intra-doc links resolved so far.
120     ///
121     /// Links are indexed by the DefId of the item they document.
122     crate intra_doc_links: FxHashMap<ItemId, Vec<clean::ItemLink>>,
123     /// Cfg that have been hidden via #![doc(cfg_hide(...))]
124     crate hidden_cfg: FxHashSet<clean::cfg::Cfg>,
125 }
126
127 /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.
128 struct CacheBuilder<'a, 'tcx> {
129     cache: &'a mut Cache,
130     tcx: TyCtxt<'tcx>,
131 }
132
133 impl Cache {
134     crate fn new(access_levels: AccessLevels<DefId>, document_private: bool) -> Self {
135         Cache { access_levels, document_private, ..Cache::default() }
136     }
137
138     /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was
139     /// in `krate` due to the data being moved into the `Cache`.
140     crate fn populate(cx: &mut DocContext<'_>, mut krate: clean::Crate) -> clean::Crate {
141         let tcx = cx.tcx;
142         let render_options = &cx.render_options;
143
144         // Crawl the crate to build various caches used for the output
145         debug!(?cx.cache.crate_version);
146         cx.cache.traits = krate.external_traits.take();
147         let RenderOptions { extern_html_root_takes_precedence, output: dst, .. } = render_options;
148
149         // Cache where all our extern crates are located
150         // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
151         for &e in &krate.externs {
152             let name = e.name(tcx);
153             let extern_url =
154                 render_options.extern_html_root_urls.get(&*name.as_str()).map(|u| &**u);
155             let location = e.location(extern_url, *extern_html_root_takes_precedence, dst, tcx);
156             cx.cache.extern_locations.insert(e.crate_num, location);
157             cx.cache.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module));
158         }
159
160         // FIXME: avoid this clone (requires implementing Default manually)
161         cx.cache.primitive_locations = PrimitiveType::primitive_locations(tcx).clone();
162         for (prim, &def_id) in &cx.cache.primitive_locations {
163             let crate_name = tcx.crate_name(def_id.krate);
164             // Recall that we only allow primitive modules to be at the root-level of the crate.
165             // If that restriction is ever lifted, this will have to include the relative paths instead.
166             cx.cache.external_paths.insert(
167                 def_id,
168                 (vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive),
169             );
170         }
171
172         krate = CacheBuilder { tcx, cache: &mut cx.cache }.fold_crate(krate);
173
174         for (trait_did, dids, impl_) in cx.cache.orphan_trait_impls.drain(..) {
175             if cx.cache.traits.contains_key(&trait_did) {
176                 for did in dids {
177                     cx.cache.impls.entry(did).or_default().push(impl_.clone());
178                 }
179             }
180         }
181
182         krate
183     }
184 }
185
186 impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
187     fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
188         if item.def_id.is_local() {
189             debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id);
190         }
191
192         // If this is a stripped module,
193         // we don't want it or its children in the search index.
194         let orig_stripped_mod = match *item.kind {
195             clean::StrippedItem(box clean::ModuleItem(..)) => {
196                 mem::replace(&mut self.cache.stripped_mod, true)
197             }
198             _ => self.cache.stripped_mod,
199         };
200
201         // If the impl is from a masked crate or references something from a
202         // masked crate then remove it completely.
203         if let clean::ImplItem(ref i) = *item.kind {
204             if self.cache.masked_crates.contains(&item.def_id.krate())
205                 || i.trait_
206                     .as_ref()
207                     .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
208                 || i.for_
209                     .def_id(self.cache)
210                     .map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
211             {
212                 return None;
213             }
214         }
215
216         // Propagate a trait method's documentation to all implementors of the
217         // trait.
218         if let clean::TraitItem(ref t) = *item.kind {
219             self.cache.traits.entry(item.def_id.expect_def_id()).or_insert_with(|| {
220                 clean::TraitWithExtraInfo {
221                     trait_: t.clone(),
222                     is_notable: item.attrs.has_doc_flag(sym::notable_trait),
223                 }
224             });
225         }
226
227         // Collect all the implementors of traits.
228         if let clean::ImplItem(ref i) = *item.kind {
229             if let Some(trait_) = &i.trait_ {
230                 if !i.kind.is_blanket() {
231                     self.cache
232                         .implementors
233                         .entry(trait_.def_id())
234                         .or_default()
235                         .push(Impl { impl_item: item.clone() });
236                 }
237             }
238         }
239
240         // Index this method for searching later on.
241         if let Some(ref s) = item.name {
242             let (parent, is_inherent_impl_item) = match *item.kind {
243                 clean::StrippedItem(..) => ((None, None), false),
244                 clean::AssocConstItem(..) | clean::TypedefItem(_, true)
245                     if self.cache.parent_is_trait_impl =>
246                 {
247                     // skip associated items in trait impls
248                     ((None, None), false)
249                 }
250                 clean::AssocTypeItem(..)
251                 | clean::TyMethodItem(..)
252                 | clean::StructFieldItem(..)
253                 | clean::VariantItem(..) => (
254                     (
255                         Some(*self.cache.parent_stack.last().expect("parent_stack is empty")),
256                         Some(&self.cache.stack[..self.cache.stack.len() - 1]),
257                     ),
258                     false,
259                 ),
260                 clean::MethodItem(..) | clean::AssocConstItem(..) => {
261                     if self.cache.parent_stack.is_empty() {
262                         ((None, None), false)
263                     } else {
264                         let last = self.cache.parent_stack.last().expect("parent_stack is empty 2");
265                         let did = *last;
266                         let path = match self.cache.paths.get(&did) {
267                             // The current stack not necessarily has correlation
268                             // for where the type was defined. On the other
269                             // hand, `paths` always has the right
270                             // information if present.
271                             Some(&(
272                                 ref fqp,
273                                 ItemType::Trait
274                                 | ItemType::Struct
275                                 | ItemType::Union
276                                 | ItemType::Enum,
277                             )) => Some(&fqp[..fqp.len() - 1]),
278                             Some(..) => Some(&*self.cache.stack),
279                             None => None,
280                         };
281                         ((Some(*last), path), true)
282                     }
283                 }
284                 _ => ((None, Some(&*self.cache.stack)), false),
285             };
286
287             match parent {
288                 (parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => {
289                     debug_assert!(!item.is_stripped());
290
291                     // A crate has a module at its root, containing all items,
292                     // which should not be indexed. The crate-item itself is
293                     // inserted later on when serializing the search-index.
294                     if item.def_id.index().map_or(false, |idx| idx != CRATE_DEF_INDEX) {
295                         let desc = item.doc_value().map_or_else(String::new, |x| {
296                             short_markdown_summary(x.as_str(), &item.link_names(self.cache))
297                         });
298                         self.cache.search_index.push(IndexItem {
299                             ty: item.type_(),
300                             name: s.to_string(),
301                             path: path.join("::"),
302                             desc,
303                             parent,
304                             parent_idx: None,
305                             search_type: get_index_search_type(&item, self.tcx, self.cache),
306                             aliases: item.attrs.get_doc_aliases(),
307                         });
308                     }
309                 }
310                 (Some(parent), None) if is_inherent_impl_item => {
311                     // We have a parent, but we don't know where they're
312                     // defined yet. Wait for later to index this item.
313                     self.cache.orphan_impl_items.push((parent, item.clone()));
314                 }
315                 _ => {}
316             }
317         }
318
319         // Keep track of the fully qualified path for this item.
320         let pushed = match item.name {
321             Some(n) if !n.is_empty() => {
322                 self.cache.stack.push(n.to_string());
323                 true
324             }
325             _ => false,
326         };
327
328         match *item.kind {
329             clean::StructItem(..)
330             | clean::EnumItem(..)
331             | clean::TypedefItem(..)
332             | clean::TraitItem(..)
333             | clean::TraitAliasItem(..)
334             | clean::FunctionItem(..)
335             | clean::ModuleItem(..)
336             | clean::ForeignFunctionItem(..)
337             | clean::ForeignStaticItem(..)
338             | clean::ConstantItem(..)
339             | clean::StaticItem(..)
340             | clean::UnionItem(..)
341             | clean::ForeignTypeItem
342             | clean::MacroItem(..)
343             | clean::ProcMacroItem(..)
344             | clean::VariantItem(..) => {
345                 if !self.cache.stripped_mod {
346                     // Re-exported items mean that the same id can show up twice
347                     // in the rustdoc ast that we're looking at. We know,
348                     // however, that a re-exported item doesn't show up in the
349                     // `public_items` map, so we can skip inserting into the
350                     // paths map if there was already an entry present and we're
351                     // not a public item.
352                     if !self.cache.paths.contains_key(&item.def_id.expect_def_id())
353                         || self.cache.access_levels.is_public(item.def_id.expect_def_id())
354                     {
355                         self.cache.paths.insert(
356                             item.def_id.expect_def_id(),
357                             (self.cache.stack.clone(), item.type_()),
358                         );
359                     }
360                 }
361             }
362             clean::PrimitiveItem(..) => {
363                 self.cache
364                     .paths
365                     .insert(item.def_id.expect_def_id(), (self.cache.stack.clone(), item.type_()));
366             }
367
368             clean::ExternCrateItem { .. }
369             | clean::ImportItem(..)
370             | clean::OpaqueTyItem(..)
371             | clean::ImplItem(..)
372             | clean::TyMethodItem(..)
373             | clean::MethodItem(..)
374             | clean::StructFieldItem(..)
375             | clean::AssocConstItem(..)
376             | clean::AssocTypeItem(..)
377             | clean::StrippedItem(..)
378             | clean::KeywordItem(..) => {
379                 // FIXME: Do these need handling?
380                 // The person writing this comment doesn't know.
381                 // So would rather leave them to an expert,
382                 // as at least the list is better than `_ => {}`.
383             }
384         }
385
386         // Maintain the parent stack
387         let orig_parent_is_trait_impl = self.cache.parent_is_trait_impl;
388         let parent_pushed = match *item.kind {
389             clean::TraitItem(..)
390             | clean::EnumItem(..)
391             | clean::ForeignTypeItem
392             | clean::StructItem(..)
393             | clean::UnionItem(..)
394             | clean::VariantItem(..) => {
395                 self.cache.parent_stack.push(item.def_id.expect_def_id());
396                 self.cache.parent_is_trait_impl = false;
397                 true
398             }
399             clean::ImplItem(ref i) => {
400                 self.cache.parent_is_trait_impl = i.trait_.is_some();
401                 match i.for_ {
402                     clean::ResolvedPath { did, .. } => {
403                         self.cache.parent_stack.push(did);
404                         true
405                     }
406                     clean::DynTrait(ref bounds, _)
407                     | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
408                         self.cache.parent_stack.push(bounds[0].trait_.def_id());
409                         true
410                     }
411                     ref t => {
412                         let prim_did = t
413                             .primitive_type()
414                             .and_then(|t| self.cache.primitive_locations.get(&t).cloned());
415                         match prim_did {
416                             Some(did) => {
417                                 self.cache.parent_stack.push(did);
418                                 true
419                             }
420                             None => false,
421                         }
422                     }
423                 }
424             }
425             _ => false,
426         };
427
428         // Once we've recursively found all the generics, hoard off all the
429         // implementations elsewhere.
430         let item = self.fold_item_recur(item);
431         let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item {
432             // Figure out the id of this impl. This may map to a
433             // primitive rather than always to a struct/enum.
434             // Note: matching twice to restrict the lifetime of the `i` borrow.
435             let mut dids = FxHashSet::default();
436             match i.for_ {
437                 clean::ResolvedPath { did, .. }
438                 | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => {
439                     dids.insert(did);
440                 }
441                 clean::DynTrait(ref bounds, _)
442                 | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
443                     dids.insert(bounds[0].trait_.def_id());
444                 }
445                 ref t => {
446                     let did = t
447                         .primitive_type()
448                         .and_then(|t| self.cache.primitive_locations.get(&t).cloned());
449
450                     if let Some(did) = did {
451                         dids.insert(did);
452                     }
453                 }
454             }
455
456             if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
457                 for bound in generics {
458                     if let Some(did) = bound.def_id(self.cache) {
459                         dids.insert(did);
460                     }
461                 }
462             }
463             let impl_item = Impl { impl_item: item };
464             if impl_item.trait_did().map_or(true, |d| self.cache.traits.contains_key(&d)) {
465                 for did in dids {
466                     self.cache.impls.entry(did).or_insert_with(Vec::new).push(impl_item.clone());
467                 }
468             } else {
469                 let trait_did = impl_item.trait_did().expect("no trait did");
470                 self.cache.orphan_trait_impls.push((trait_did, dids, impl_item));
471             }
472             None
473         } else {
474             Some(item)
475         };
476
477         if pushed {
478             self.cache.stack.pop().expect("stack already empty");
479         }
480         if parent_pushed {
481             self.cache.parent_stack.pop().expect("parent stack already empty");
482         }
483         self.cache.stripped_mod = orig_stripped_mod;
484         self.cache.parent_is_trait_impl = orig_parent_is_trait_impl;
485         ret
486     }
487 }