diff options
Diffstat (limited to 'src/librustdoc/html/render/cache.rs')
| -rw-r--r-- | src/librustdoc/html/render/cache.rs | 500 |
1 files changed, 12 insertions, 488 deletions
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index 1b5c8a9378e..378efa1a1be 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -1,18 +1,16 @@ -use crate::clean::{self, AttributesExt, GetDefId}; -use crate::fold::DocFolder; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; -use rustc_middle::middle::privacy::AccessLevels; -use rustc_span::source_map::FileName; -use rustc_span::symbol::sym; use std::collections::BTreeMap; -use std::mem; -use std::path::{Path, PathBuf}; +use std::path::Path; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::symbol::sym; use serde::Serialize; -use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}; -use super::{Generic, RenderInfo, RenderType, TypeWithKind}; +use crate::clean::types::GetDefId; +use crate::clean::{self, AttributesExt}; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::html::render::{plain_summary_line, shorten}; +use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind}; /// Indicates where an external crate can be found. pub enum ExternalLocation { @@ -24,483 +22,9 @@ pub enum ExternalLocation { Unknown, } -/// This cache is used to store information about the `clean::Crate` being -/// rendered in order to provide more useful documentation. This contains -/// information like all implementors of a trait, all traits a type implements, -/// documentation for all known traits, etc. -/// -/// This structure purposefully does not implement `Clone` because it's intended -/// to be a fairly large and expensive structure to clone. Instead this adheres -/// to `Send` so it may be stored in a `Arc` instance and shared among the various -/// rendering threads. -#[derive(Default)] -crate struct Cache { - /// Maps a type ID to all known implementations for that type. This is only - /// recognized for intra-crate `ResolvedPath` types, and is used to print - /// out extra documentation on the page of an enum/struct. - /// - /// The values of the map are a list of implementations and documentation - /// found on that implementation. - pub impls: FxHashMap<DefId, Vec<Impl>>, - - /// Maintains a mapping of local crate `DefId`s to the fully qualified name - /// and "short type description" of that node. This is used when generating - /// URLs when a type is being linked to. External paths are not located in - /// this map because the `External` type itself has all the information - /// necessary. - pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>, - - /// Similar to `paths`, but only holds external paths. This is only used for - /// generating explicit hyperlinks to other crates. - pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, - - /// Maps local `DefId`s of exported types to fully qualified paths. - /// Unlike 'paths', this mapping ignores any renames that occur - /// due to 'use' statements. - /// - /// This map is used when writing out the special 'implementors' - /// javascript file. By using the exact path that the type - /// is declared with, we ensure that each path will be identical - /// to the path used if the corresponding type is inlined. By - /// doing this, we can detect duplicate impls on a trait page, and only display - /// the impl for the inlined type. - pub exact_paths: FxHashMap<DefId, Vec<String>>, - - /// This map contains information about all known traits of this crate. - /// Implementations of a crate should inherit the documentation of the - /// parent trait if no extra documentation is specified, and default methods - /// should show up in documentation about trait implementations. - pub traits: FxHashMap<DefId, clean::Trait>, - - /// When rendering traits, it's often useful to be able to list all - /// implementors of the trait, and this mapping is exactly, that: a mapping - /// of trait ids to the list of known implementors of the trait - pub implementors: FxHashMap<DefId, Vec<Impl>>, - - /// Cache of where external crate documentation can be found. - pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>, - - /// Cache of where documentation for primitives can be found. - pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>, - - // Note that external items for which `doc(hidden)` applies to are shown as - // non-reachable while local items aren't. This is because we're reusing - // the access levels from the privacy check pass. - pub access_levels: AccessLevels<DefId>, - - /// The version of the crate being documented, if given from the `--crate-version` flag. - pub crate_version: Option<String>, - - /// Whether to document private items. - /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. - pub document_private: bool, - - // Private fields only used when initially crawling a crate to build a cache - stack: Vec<String>, - parent_stack: Vec<DefId>, - parent_is_trait_impl: bool, - search_index: Vec<IndexItem>, - stripped_mod: bool, - pub deref_trait_did: Option<DefId>, - pub deref_mut_trait_did: Option<DefId>, - pub owned_box_did: Option<DefId>, - masked_crates: FxHashSet<CrateNum>, - - // In rare case where a structure is defined in one module but implemented - // in another, if the implementing module is parsed before defining module, - // then the fully qualified name of the structure isn't presented in `paths` - // yet when its implementation methods are being indexed. Caches such methods - // and their parent id here and indexes them at the end of crate parsing. - orphan_impl_items: Vec<(DefId, clean::Item)>, - - // Similarly to `orphan_impl_items`, sometimes trait impls are picked up - // even though the trait itself is not exported. This can happen if a trait - // was defined in function/expression scope, since the impl will be picked - // up by `collect-trait-impls` but the trait won't be scraped out in the HIR - // crawl. In order to prevent crashes when looking for spotlight traits or - // when gathering trait documentation on a type, hold impls here while - // folding and add them to the cache later on if we find the trait. - orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>, - - /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, - /// we need the alias element to have an array of items. - pub(super) aliases: BTreeMap<String, Vec<usize>>, -} - -impl Cache { - pub fn from_krate( - renderinfo: RenderInfo, - document_private: bool, - extern_html_root_urls: &BTreeMap<String, String>, - dst: &Path, - mut krate: clean::Crate, - ) -> (clean::Crate, String, Cache) { - // Crawl the crate to build various caches used for the output - let RenderInfo { - inlined: _, - external_paths, - exact_paths, - access_levels, - deref_trait_did, - deref_mut_trait_did, - owned_box_did, - .. - } = renderinfo; - - let external_paths = - external_paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from(t)))).collect(); - - let mut cache = Cache { - impls: Default::default(), - external_paths, - exact_paths, - paths: Default::default(), - implementors: Default::default(), - stack: Vec::new(), - parent_stack: Vec::new(), - search_index: Vec::new(), - parent_is_trait_impl: false, - extern_locations: Default::default(), - primitive_locations: Default::default(), - stripped_mod: false, - access_levels, - crate_version: krate.version.take(), - document_private, - orphan_impl_items: Vec::new(), - orphan_trait_impls: Vec::new(), - traits: krate.external_traits.replace(Default::default()), - deref_trait_did, - deref_mut_trait_did, - owned_box_did, - masked_crates: mem::take(&mut krate.masked_crates), - aliases: Default::default(), - }; - - // Cache where all our extern crates are located - for &(n, ref e) in &krate.externs { - let src_root = match e.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u); - cache - .extern_locations - .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst))); - - let did = DefId { krate: n, index: CRATE_DEF_INDEX }; - cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); - } - - // Cache where all known primitives have their documentation located. - // - // Favor linking to as local extern as possible, so iterate all crates in - // reverse topological order. - for &(_, ref e) in krate.externs.iter().rev() { - for &(def_id, prim, _) in &e.primitives { - cache.primitive_locations.insert(prim, def_id); - } - } - for &(def_id, prim, _) in &krate.primitives { - cache.primitive_locations.insert(prim, def_id); - } - - cache.stack.push(krate.name.clone()); - krate = cache.fold_crate(krate); - - for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { - if cache.traits.contains_key(&trait_did) { - for did in dids { - cache.impls.entry(did).or_insert(vec![]).push(impl_.clone()); - } - } - } - - // Build our search index - let index = build_index(&krate, &mut cache); - - (krate, index, cache) - } -} - -impl DocFolder for Cache { - fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { - if item.def_id.is_local() { - debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id); - } - - // If this is a stripped module, - // we don't want it or its children in the search index. - let orig_stripped_mod = match item.inner { - clean::StrippedItem(box clean::ModuleItem(..)) => { - mem::replace(&mut self.stripped_mod, true) - } - _ => self.stripped_mod, - }; - - // If the impl is from a masked crate or references something from a - // masked crate then remove it completely. - if let clean::ImplItem(ref i) = item.inner { - if self.masked_crates.contains(&item.def_id.krate) - || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) - || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) - { - return None; - } - } - - // Propagate a trait method's documentation to all implementors of the - // trait. - if let clean::TraitItem(ref t) = item.inner { - self.traits.entry(item.def_id).or_insert_with(|| t.clone()); - } - - // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = item.inner { - if let Some(did) = i.trait_.def_id() { - if i.blanket_impl.is_none() { - self.implementors - .entry(did) - .or_default() - .push(Impl { impl_item: item.clone() }); - } - } - } - - // Index this method for searching later on. - if let Some(ref s) = item.name { - let (parent, is_inherent_impl_item) = match item.inner { - clean::StrippedItem(..) => ((None, None), false), - clean::AssocConstItem(..) | clean::TypedefItem(_, true) - if self.parent_is_trait_impl => - { - // skip associated items in trait impls - ((None, None), false) - } - clean::AssocTypeItem(..) - | clean::TyMethodItem(..) - | clean::StructFieldItem(..) - | clean::VariantItem(..) => ( - ( - Some(*self.parent_stack.last().expect("parent_stack is empty")), - Some(&self.stack[..self.stack.len() - 1]), - ), - false, - ), - clean::MethodItem(..) | clean::AssocConstItem(..) => { - if self.parent_stack.is_empty() { - ((None, None), false) - } else { - let last = self.parent_stack.last().expect("parent_stack is empty 2"); - let did = *last; - let path = match self.paths.get(&did) { - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - Some(&( - ref fqp, - ItemType::Trait - | ItemType::Struct - | ItemType::Union - | ItemType::Enum, - )) => Some(&fqp[..fqp.len() - 1]), - Some(..) => Some(&*self.stack), - None => None, - }; - ((Some(*last), path), true) - } - } - _ => ((None, Some(&*self.stack)), false), - }; - - match parent { - (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { - debug_assert!(!item.is_stripped()); - - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.def_id.index != CRATE_DEF_INDEX { - self.search_index.push(IndexItem { - ty: item.type_(), - name: s.to_string(), - path: path.join("::"), - desc: shorten(plain_summary_line(item.doc_value())), - parent, - parent_idx: None, - search_type: get_index_search_type(&item), - }); - - for alias in item.attrs.get_doc_aliases() { - self.aliases - .entry(alias.to_lowercase()) - .or_insert(Vec::new()) - .push(self.search_index.len() - 1); - } - } - } - (Some(parent), None) if is_inherent_impl_item => { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - self.orphan_impl_items.push((parent, item.clone())); - } - _ => {} - } - } - - // Keep track of the fully qualified path for this item. - let pushed = match item.name { - Some(ref n) if !n.is_empty() => { - self.stack.push(n.to_string()); - true - } - _ => false, - }; - - match item.inner { - clean::StructItem(..) - | clean::EnumItem(..) - | clean::TypedefItem(..) - | clean::TraitItem(..) - | clean::FunctionItem(..) - | clean::ModuleItem(..) - | clean::ForeignFunctionItem(..) - | clean::ForeignStaticItem(..) - | clean::ConstantItem(..) - | clean::StaticItem(..) - | clean::UnionItem(..) - | clean::ForeignTypeItem - | clean::MacroItem(..) - | clean::ProcMacroItem(..) - | clean::VariantItem(..) - if !self.stripped_mod => - { - // Re-exported items mean that the same id can show up twice - // in the rustdoc ast that we're looking at. We know, - // however, that a re-exported item doesn't show up in the - // `public_items` map, so we can skip inserting into the - // paths map if there was already an entry present and we're - // not a public item. - if !self.paths.contains_key(&item.def_id) - || self.access_levels.is_public(item.def_id) - { - self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); - } - } - clean::PrimitiveItem(..) => { - self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); - } - - _ => {} - } - - // Maintain the parent stack - let orig_parent_is_trait_impl = self.parent_is_trait_impl; - let parent_pushed = match item.inner { - clean::TraitItem(..) - | clean::EnumItem(..) - | clean::ForeignTypeItem - | clean::StructItem(..) - | clean::UnionItem(..) - | clean::VariantItem(..) => { - self.parent_stack.push(item.def_id); - self.parent_is_trait_impl = false; - true - } - clean::ImplItem(ref i) => { - self.parent_is_trait_impl = i.trait_.is_some(); - match i.for_ { - clean::ResolvedPath { did, .. } => { - self.parent_stack.push(did); - true - } - ref t => { - let prim_did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); - match prim_did { - Some(did) => { - self.parent_stack.push(did); - true - } - None => false, - } - } - } - } - _ => false, - }; - - // Once we've recursively found all the generics, hoard off all the - // implementations elsewhere. - let ret = self.fold_item_recur(item).and_then(|item| { - if let clean::Item { inner: clean::ImplItem(_), .. } = item { - // Figure out the id of this impl. This may map to a - // primitive rather than always to a struct/enum. - // Note: matching twice to restrict the lifetime of the `i` borrow. - let mut dids = FxHashSet::default(); - if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { - match i.for_ { - clean::ResolvedPath { did, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { did, .. }, .. - } => { - dids.insert(did); - } - ref t => { - let did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); - - if let Some(did) = did { - dids.insert(did); - } - } - } - - if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { - for bound in generics { - if let Some(did) = bound.def_id() { - dids.insert(did); - } - } - } - } else { - unreachable!() - }; - let impl_item = Impl { impl_item: item }; - if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { - for did in dids { - self.impls.entry(did).or_insert(vec![]).push(impl_item.clone()); - } - } else { - let trait_did = impl_item.trait_did().expect("no trait did"); - self.orphan_trait_impls.push((trait_did, dids, impl_item)); - } - None - } else { - Some(item) - } - }); - - if pushed { - self.stack.pop().expect("stack already empty"); - } - if parent_pushed { - self.parent_stack.pop().expect("parent stack already empty"); - } - self.stripped_mod = orig_stripped_mod; - self.parent_is_trait_impl = orig_parent_is_trait_impl; - ret - } -} - /// Attempts to find where an external crate is located, given that we're /// rendering in to the specified source destination. -fn extern_location( +pub fn extern_location( e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path, @@ -538,7 +62,7 @@ fn extern_location( } /// Builds the search index from the collected metadata -fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { +pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let mut defid_to_pathid = FxHashMap::default(); let mut crate_items = Vec::with_capacity(cache.search_index.len()); let mut crate_paths = vec![]; @@ -640,7 +164,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ) } -fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> { +crate fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> { let (all_types, ret_types) = match item.inner { clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types), clean::MethodItem(ref m) => (&m.all_types, &m.ret_types), |
