summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/passes/collect_intra_doc_links/early.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/passes/collect_intra_doc_links/early.rs')
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs407
1 files changed, 0 insertions, 407 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
deleted file mode 100644
index 42677bd84..000000000
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ /dev/null
@@ -1,407 +0,0 @@
-use crate::clean::Attributes;
-use crate::core::ResolverCaches;
-use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
-use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
-
-use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{self as ast, ItemKind};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def::Namespace::*;
-use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
-use rustc_hir::TraitCandidate;
-use rustc_middle::ty::{DefIdTree, Visibility};
-use rustc_resolve::{ParentScope, Resolver};
-use rustc_span::symbol::sym;
-use rustc_span::{Symbol, SyntaxContext};
-
-use std::collections::hash_map::Entry;
-use std::mem;
-
-pub(crate) fn early_resolve_intra_doc_links(
- resolver: &mut Resolver<'_>,
- krate: &ast::Crate,
- document_private_items: bool,
-) -> ResolverCaches {
- let parent_scope =
- ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
- let mut link_resolver = EarlyDocLinkResolver {
- resolver,
- parent_scope,
- visited_mods: Default::default(),
- markdown_links: Default::default(),
- doc_link_resolutions: Default::default(),
- traits_in_scope: Default::default(),
- all_trait_impls: Default::default(),
- all_macro_rules: Default::default(),
- document_private_items,
- };
-
- // Overridden `visit_item` below doesn't apply to the crate root,
- // so we have to visit its attributes and reexports separately.
- link_resolver.resolve_doc_links_local(&krate.attrs);
- link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
- visit::walk_crate(&mut link_resolver, krate);
-
- // FIXME: somehow rustdoc is still missing crates even though we loaded all
- // the known necessary crates. Load them all unconditionally until we find a way to fix this.
- // DO NOT REMOVE THIS without first testing on the reproducer in
- // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
- for (extern_name, _) in
- link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude)
- {
- link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
- }
-
- link_resolver.process_extern_impls();
-
- ResolverCaches {
- markdown_links: Some(link_resolver.markdown_links),
- doc_link_resolutions: link_resolver.doc_link_resolutions,
- traits_in_scope: link_resolver.traits_in_scope,
- all_trait_impls: Some(link_resolver.all_trait_impls),
- all_macro_rules: link_resolver.all_macro_rules,
- }
-}
-
-fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
- Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
-}
-
-struct EarlyDocLinkResolver<'r, 'ra> {
- resolver: &'r mut Resolver<'ra>,
- parent_scope: ParentScope<'ra>,
- visited_mods: DefIdSet,
- markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
- doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
- traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
- all_trait_impls: Vec<DefId>,
- all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
- document_private_items: bool,
-}
-
-impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
- fn add_traits_in_scope(&mut self, def_id: DefId) {
- // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
- // Keys in the `traits_in_scope` cache are always module IDs.
- if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) {
- let module = self.resolver.get_nearest_non_block_module(def_id);
- let module_id = module.def_id();
- let entry = if module_id == def_id {
- Some(entry)
- } else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) {
- Some(entry)
- } else {
- None
- };
- if let Some(entry) = entry {
- entry.insert(self.resolver.traits_in_scope(
- None,
- &ParentScope::module(module, self.resolver),
- SyntaxContext::root(),
- None,
- ));
- }
- }
- }
-
- /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
- /// That pass filters impls using type-based information, but we don't yet have such
- /// information here, so we just conservatively calculate traits in scope for *all* modules
- /// having impls in them.
- fn process_extern_impls(&mut self) {
- // Resolving links in already existing crates may trigger loading of new crates.
- let mut start_cnum = 0;
- loop {
- let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
- for &cnum in &crates[start_cnum..] {
- let all_trait_impls =
- Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
- let all_inherent_impls =
- Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
- let all_incoherent_impls = Vec::from_iter(
- self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum),
- );
-
- // Querying traits in scope is expensive so we try to prune the impl lists using
- // privacy, private traits and impls from other crates are never documented in
- // the current crate, and links in their doc comments are not resolved.
- for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
- if self.resolver.cstore().visibility_untracked(trait_def_id).is_public()
- && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
- self.resolver.cstore().visibility_untracked(ty_def_id).is_public()
- })
- {
- if self.visited_mods.insert(trait_def_id) {
- self.resolve_doc_links_extern_impl(trait_def_id, false);
- }
- self.resolve_doc_links_extern_impl(impl_def_id, false);
- }
- }
- for (ty_def_id, impl_def_id) in all_inherent_impls {
- if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() {
- self.resolve_doc_links_extern_impl(impl_def_id, true);
- }
- }
- for impl_def_id in all_incoherent_impls {
- self.resolve_doc_links_extern_impl(impl_def_id, true);
- }
-
- self.all_trait_impls
- .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
- }
-
- if crates.len() > start_cnum {
- start_cnum = crates.len();
- } else {
- break;
- }
- }
- }
-
- fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
- self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
- let assoc_item_def_ids = Vec::from_iter(
- self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()),
- );
- for assoc_def_id in assoc_item_def_ids {
- if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
- {
- self.resolve_doc_links_extern_outer_fixme(assoc_def_id, def_id);
- }
- }
- }
-
- // FIXME: replace all uses with `resolve_doc_links_extern_outer` to actually resolve links, not
- // just add traits in scope. This may be expensive and require benchmarking and optimization.
- fn resolve_doc_links_extern_outer_fixme(&mut self, def_id: DefId, scope_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
- self.add_traits_in_scope(parent_id);
- }
- }
-
- fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- let attrs = Vec::from_iter(
- self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
- );
- let parent_scope = ParentScope::module(
- self.resolver.get_nearest_non_block_module(
- self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
- ),
- self.resolver,
- );
- self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
- }
-
- fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- let attrs = Vec::from_iter(
- self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
- );
- let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
- self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
- }
-
- fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
- if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
- return;
- }
- self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
- }
-
- fn resolve_and_cache(
- &mut self,
- path_str: &str,
- ns: Namespace,
- parent_scope: &ParentScope<'ra>,
- ) -> bool {
- // FIXME: This caching may be incorrect in case of multiple `macro_rules`
- // items with the same name in the same module.
- self.doc_link_resolutions
- .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
- .or_insert_with_key(|(path, ns, _)| {
- self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
- })
- .is_some()
- }
-
- fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
- let mut need_traits_in_scope = false;
- for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
- assert_eq!(doc_module, None);
- let mut tmp_links = mem::take(&mut self.markdown_links);
- let links =
- tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
- for PreprocessedMarkdownLink(pp_link, _) in links {
- if let Ok(pinfo) = pp_link {
- // The logic here is a conservative approximation for path resolution in
- // `resolve_with_disambiguator`.
- if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
- if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
- continue;
- }
- }
-
- // Resolve all namespaces due to no disambiguator or for diagnostics.
- let mut any_resolved = false;
- let mut need_assoc = false;
- for ns in [TypeNS, ValueNS, MacroNS] {
- if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
- any_resolved = true;
- } else if ns != MacroNS {
- need_assoc = true;
- }
- }
-
- // Resolve all prefixes for type-relative resolution or for diagnostics.
- if need_assoc || !any_resolved {
- let mut path = &pinfo.path_str[..];
- while let Some(idx) = path.rfind("::") {
- path = &path[..idx];
- need_traits_in_scope = true;
- for ns in [TypeNS, ValueNS, MacroNS] {
- self.resolve_and_cache(path, ns, &parent_scope);
- }
- }
- }
- }
- }
- self.markdown_links = tmp_links;
- }
-
- if need_traits_in_scope {
- self.add_traits_in_scope(parent_scope.module.def_id());
- }
- }
-
- /// When reexports are inlined, they are replaced with item which they refer to, those items
- /// may have links in their doc comments, those links are resolved at the item definition site,
- /// so we need to know traits in scope at that definition site.
- fn process_module_children_or_reexports(&mut self, module_id: DefId) {
- if !self.visited_mods.insert(module_id) {
- return; // avoid infinite recursion
- }
-
- for child in self.resolver.module_children_or_reexports(module_id) {
- // This condition should give a superset of `denied` from `fn clean_use_statement`.
- if child.vis.is_public()
- || self.document_private_items
- && child.vis != Visibility::Restricted(module_id)
- && module_id.is_local()
- {
- if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
- let scope_id = match child.res {
- Res::Def(
- DefKind::Variant
- | DefKind::AssocTy
- | DefKind::AssocFn
- | DefKind::AssocConst,
- ..,
- ) => self.resolver.parent(def_id),
- _ => def_id,
- };
- self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
- if let Res::Def(DefKind::Mod, ..) = child.res {
- self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
- }
- if let Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, ..) = child.res {
- self.process_module_children_or_reexports(def_id);
- }
- if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
- child.res
- {
- let field_def_ids = Vec::from_iter(
- self.resolver
- .cstore()
- .associated_item_def_ids_untracked(def_id, self.resolver.sess()),
- );
- for field_def_id in field_def_ids {
- self.resolve_doc_links_extern_outer(field_def_id, scope_id);
- }
- }
- }
- }
- }
- }
-}
-
-impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
- fn visit_item(&mut self, item: &ast::Item) {
- self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
- if let ItemKind::Mod(..) = item.kind {
- let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
- let module = self.resolver.expect_module(module_def_id);
- let old_module = mem::replace(&mut self.parent_scope.module, module);
- let old_macro_rules = self.parent_scope.macro_rules;
- self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
- self.process_module_children_or_reexports(module_def_id);
- visit::walk_item(self, item);
- if item
- .attrs
- .iter()
- .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
- {
- self.parent_scope.macro_rules = old_macro_rules;
- }
- self.parent_scope.module = old_module;
- } else {
- match &item.kind {
- ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => {
- if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id)
- && let Some(res) = partial_res.full_res()
- && let Some(trait_def_id) = res.opt_def_id()
- && !trait_def_id.is_local()
- && self.visited_mods.insert(trait_def_id) {
- self.resolve_doc_links_extern_impl(trait_def_id, false);
- }
- self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
- }
- ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
- let (macro_rules_scope, res) =
- self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
- self.parent_scope.macro_rules = macro_rules_scope;
- self.all_macro_rules.insert(item.ident.name, res);
- }
- _ => {}
- }
- visit::walk_item(self, item);
- }
- }
-
- fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
- self.resolve_doc_links_local(&item.attrs);
- visit::walk_assoc_item(self, item, ctxt)
- }
-
- fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
- self.resolve_doc_links_local(&item.attrs);
- visit::walk_foreign_item(self, item)
- }
-
- fn visit_variant(&mut self, v: &ast::Variant) {
- self.resolve_doc_links_local(&v.attrs);
- visit::walk_variant(self, v)
- }
-
- fn visit_field_def(&mut self, field: &ast::FieldDef) {
- self.resolve_doc_links_local(&field.attrs);
- visit::walk_field_def(self, field)
- }
-
- fn visit_block(&mut self, block: &ast::Block) {
- let old_macro_rules = self.parent_scope.macro_rules;
- visit::walk_block(self, block);
- self.parent_scope.macro_rules = old_macro_rules;
- }
-
- // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
- // then this will have to implement other visitor methods too.
-}