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