-use crate::clean;
+use crate::clean::Attributes;
use crate::core::ResolverCaches;
use crate::html::markdown::markdown_links;
use crate::passes::collect_intra_doc_links::preprocess_link;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_resolve::{ParentScope, Resolver};
use rustc_session::config::Externs;
-use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+use rustc_span::SyntaxContext;
use std::collections::hash_map::Entry;
use std::mem;
resolver: &mut Resolver<'_>,
krate: &ast::Crate,
externs: Externs,
+ document_private_items: bool,
) -> ResolverCaches {
- let mut loader = IntraLinkCrateLoader {
+ let mut link_resolver = EarlyDocLinkResolver {
resolver,
current_mod: CRATE_DEF_ID,
visited_mods: Default::default(),
traits_in_scope: Default::default(),
all_traits: Default::default(),
all_trait_impls: Default::default(),
+ document_private_items,
};
- // Because of the `crate::` prefix, any doc comment can reference
- // the crate root's set of in-scope traits. This line makes sure
- // it's available.
- loader.add_traits_in_scope(CRATE_DEF_ID.to_def_id());
-
// Overridden `visit_item` below doesn't apply to the crate root,
// so we have to visit its attributes and reexports separately.
- loader.load_links_in_attrs(&krate.attrs, krate.span);
- loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
- visit::walk_crate(&mut loader, krate);
- loader.add_foreign_traits_in_scope();
+ link_resolver.load_links_in_attrs(&krate.attrs);
+ link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
+ visit::walk_crate(&mut link_resolver, krate);
+ link_resolver.process_extern_impls();
// 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 externs.iter().filter(|(_, entry)| entry.add_prelude) {
- let _ = loader.resolver.resolve_str_path_error(
- DUMMY_SP,
- extern_name,
- TypeNS,
- CRATE_DEF_ID.to_def_id(),
- );
+ link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
}
ResolverCaches {
- traits_in_scope: loader.traits_in_scope,
- all_traits: Some(loader.all_traits),
- all_trait_impls: Some(loader.all_trait_impls),
+ traits_in_scope: link_resolver.traits_in_scope,
+ all_traits: Some(link_resolver.all_traits),
+ all_trait_impls: Some(link_resolver.all_trait_impls),
+ all_macro_rules: link_resolver.resolver.take_all_macro_rules(),
}
}
-struct IntraLinkCrateLoader<'r, 'ra> {
+struct EarlyDocLinkResolver<'r, 'ra> {
resolver: &'r mut Resolver<'ra>,
current_mod: LocalDefId,
visited_mods: DefIdSet,
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
all_traits: Vec<DefId>,
all_trait_impls: Vec<DefId>,
+ document_private_items: bool,
}
-impl IntraLinkCrateLoader<'_, '_> {
+impl EarlyDocLinkResolver<'_, '_> {
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.
/// 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 add_foreign_traits_in_scope(&mut self) {
- for cnum in Vec::from_iter(self.resolver.cstore().crates_untracked()) {
- // FIXME: Due to #78696 rustdoc can query traits in scope for any crate root.
- self.add_traits_in_scope(cnum.as_def_id());
-
- let all_traits = Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(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_lang_items = Vec::from_iter(self.resolver.cstore().lang_items_untracked(cnum));
-
- // Querying traits in scope is expensive so we try to prune the impl and traits 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 &def_id in &all_traits {
- if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
- self.add_traits_in_parent_scope(def_id);
+ fn process_extern_impls(&mut self) {
+ // FIXME: Need to resolve doc links on all these impl and trait items below.
+ // 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_traits =
+ Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(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 and traits 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 &def_id in &all_traits {
+ if self.resolver.cstore().visibility_untracked(def_id) == Visibility::Public {
+ self.add_traits_in_parent_scope(def_id);
+ }
}
- }
- for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
- if self.resolver.cstore().visibility_untracked(trait_def_id) == Visibility::Public
- && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
- self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
- })
- {
- self.add_traits_in_parent_scope(impl_def_id);
+ for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
+ if self.resolver.cstore().visibility_untracked(trait_def_id)
+ == Visibility::Public
+ && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
+ self.resolver.cstore().visibility_untracked(ty_def_id)
+ == Visibility::Public
+ })
+ {
+ self.add_traits_in_parent_scope(impl_def_id);
+ }
}
- }
- for (ty_def_id, impl_def_id) in all_inherent_impls {
- if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public {
- self.add_traits_in_parent_scope(impl_def_id);
+ for (ty_def_id, impl_def_id) in all_inherent_impls {
+ if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public
+ {
+ self.add_traits_in_parent_scope(impl_def_id);
+ }
}
- }
- for def_id in all_lang_items {
- self.add_traits_in_parent_scope(def_id);
+ for def_id in all_incoherent_impls {
+ self.add_traits_in_parent_scope(def_id);
+ }
+
+ self.all_traits.extend(all_traits);
+ self.all_trait_impls
+ .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
}
- self.all_traits.extend(all_traits);
- 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 load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
- // FIXME: this needs to consider reexport inlining.
- let attrs = clean::Attributes::from_ast(attrs, None);
- for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
- let module_id = parent_module.unwrap_or(self.current_mod.to_def_id());
-
- self.add_traits_in_scope(module_id);
-
+ fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) {
+ let module_id = self.current_mod.to_def_id();
+ let mut need_traits_in_scope = false;
+ for (doc_module, doc) in
+ Attributes::from_ast(attrs, None).collapsed_doc_value_by_module_level()
+ {
+ assert_eq!(doc_module, None);
for link in markdown_links(&doc.as_str()) {
- let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
- x.path_str
- } else {
- continue;
- };
- let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id);
+ if let Some(Ok(pinfo)) = preprocess_link(&link) {
+ self.resolver.resolve_rustdoc_path(&pinfo.path_str, TypeNS, module_id);
+ need_traits_in_scope = true;
+ }
}
}
+
+ if need_traits_in_scope {
+ self.add_traits_in_scope(module_id);
+ }
}
/// When reexports are inlined, they are replaced with item which they refer to, those items
}
for child in self.resolver.module_children_or_reexports(module_id) {
- if child.vis == Visibility::Public {
- if let Some(def_id) = child.res.opt_def_id() {
- self.add_traits_in_parent_scope(def_id);
- }
- if let Res::Def(DefKind::Mod, module_id) = child.res {
- self.process_module_children_or_reexports(module_id);
+ // This condition should give a superset of `denied` from `fn clean_use_statement`.
+ if child.vis == Visibility::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() {
+ // FIXME: Need to resolve doc links on all these extern items
+ // reached through reexports.
+ let scope_id = match child.res {
+ Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id).unwrap(),
+ _ => def_id,
+ };
+ self.add_traits_in_parent_scope(scope_id); // Outer attribute scope
+ if let Res::Def(DefKind::Mod, ..) = child.res {
+ self.add_traits_in_scope(def_id); // Inner attribute scope
+ }
+ // Traits are processed in `add_extern_traits_in_scope`.
+ if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
+ self.process_module_children_or_reexports(def_id);
+ }
}
}
}
}
}
-impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
+impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
fn visit_item(&mut self, item: &ast::Item) {
+ self.load_links_in_attrs(&item.attrs); // Outer attribute scope
if let ItemKind::Mod(..) = item.kind {
let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
-
- // A module written with a outline doc comments will resolve traits relative
- // to the parent module. Make sure the parent module's traits-in-scope are
- // loaded, even if the module itself has no doc comments.
- self.add_traits_in_parent_scope(self.current_mod.to_def_id());
-
- self.load_links_in_attrs(&item.attrs, item.span);
+ self.load_links_in_attrs(&item.attrs); // Inner attribute scope
self.process_module_children_or_reexports(self.current_mod.to_def_id());
visit::walk_item(self, item);
-
self.current_mod = old_mod;
} else {
match item.kind {
}
_ => {}
}
- self.load_links_in_attrs(&item.attrs, item.span);
visit::walk_item(self, item);
}
}
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
- self.load_links_in_attrs(&item.attrs, item.span);
+ self.load_links_in_attrs(&item.attrs);
visit::walk_assoc_item(self, item, ctxt)
}
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
- self.load_links_in_attrs(&item.attrs, item.span);
+ self.load_links_in_attrs(&item.attrs);
visit::walk_foreign_item(self, item)
}
fn visit_variant(&mut self, v: &ast::Variant) {
- self.load_links_in_attrs(&v.attrs, v.span);
+ self.load_links_in_attrs(&v.attrs);
visit::walk_variant(self, v)
}
fn visit_field_def(&mut self, field: &ast::FieldDef) {
- self.load_links_in_attrs(&field.attrs, field.span);
+ self.load_links_in_attrs(&field.attrs);
visit::walk_field_def(self, field)
}