From 957bc606dd638e28dea6c39f6678d5f9977c8cf2 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 17 Jan 2023 01:09:23 +0400 Subject: [PATCH 1/3] rustdoc: Collect rustdoc-reachable items during early doc link resolution --- .../src/rmeta/decoder/cstore_impl.rs | 6 +++ src/librustdoc/clean/utils.rs | 5 -- src/librustdoc/core.rs | 5 ++ .../passes/collect_intra_doc_links/early.rs | 22 ++++++++ src/librustdoc/visit_lib.rs | 51 ++++++++++++++++++- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index eebc2f21dfe4e..e5d0bb87edf66 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -632,6 +632,12 @@ impl CStore { .get_attr_flags(def_id.index) .contains(AttrFlags::MAY_HAVE_DOC_LINKS) } + + pub fn is_doc_hidden_untracked(&self, def_id: DefId) -> bool { + self.get_crate_data(def_id.krate) + .get_attr_flags(def_id.index) + .contains(AttrFlags::IS_DOC_HIDDEN) + } } impl CrateStore for CStore { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index a12f764fa8e3b..ca3a70c7236fe 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -29,11 +29,6 @@ mod tests; pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { let module = crate::visit_ast::RustdocVisitor::new(cx).visit(); - for &cnum in cx.tcx.crates(()) { - // Analyze doc-reachability for extern items - crate::visit_lib::lib_embargo_visit_item(cx, cnum.as_def_id()); - } - // Clean the crate, translating the entire librustc_ast AST to one that is // understood by rustdoc. let mut module = clean_doc_module(&module, cx); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 10b606f425ea4..0ce43f7db8e8b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -41,6 +41,7 @@ pub(crate) struct ResolverCaches { pub(crate) traits_in_scope: DefIdMap>, pub(crate) all_trait_impls: Option>, pub(crate) all_macro_rules: FxHashMap>, + pub(crate) extern_doc_reachable: DefIdSet, } pub(crate) struct DocContext<'tcx> { @@ -363,6 +364,10 @@ pub(crate) fn run_global_ctxt( show_coverage, }; + ctxt.cache + .effective_visibilities + .init(mem::take(&mut ctxt.resolver_caches.extern_doc_reachable)); + // Small hack to force the Sized trait to be present. // // Note that in case of `#![no_core]`, the trait is not available. diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 42677bd849748..150e53f63b9a5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -2,6 +2,7 @@ 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 crate::visit_lib::early_lib_embargo_visit_item; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, ItemKind}; @@ -34,6 +35,8 @@ pub(crate) fn early_resolve_intra_doc_links( traits_in_scope: Default::default(), all_trait_impls: Default::default(), all_macro_rules: Default::default(), + extern_doc_reachable: Default::default(), + local_doc_reachable: Default::default(), document_private_items, }; @@ -61,6 +64,7 @@ pub(crate) fn early_resolve_intra_doc_links( 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, + extern_doc_reachable: link_resolver.extern_doc_reachable, } } @@ -77,6 +81,15 @@ struct EarlyDocLinkResolver<'r, 'ra> { traits_in_scope: DefIdMap>, all_trait_impls: Vec, all_macro_rules: FxHashMap>, + /// This set is used as a seed for `effective_visibilities`, which are then extended by some + /// more items using `lib_embargo_visit_item` during doc inlining. + extern_doc_reachable: DefIdSet, + /// This is an easily identifiable superset of items added to `effective_visibilities` + /// using `lib_embargo_visit_item` during doc inlining. + /// The union of `(extern,local)_doc_reachable` is therefore a superset of + /// `effective_visibilities` and can be used for pruning extern impls here + /// in early doc link resolution. + local_doc_reachable: DefIdSet, document_private_items: bool, } @@ -114,6 +127,14 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { let mut start_cnum = 0; loop { let crates = Vec::from_iter(self.resolver.cstore().crates_untracked()); + for cnum in &crates[start_cnum..] { + early_lib_embargo_visit_item( + self.resolver, + &mut self.extern_doc_reachable, + cnum.as_def_id(), + true, + ); + } for &cnum in &crates[start_cnum..] { let all_trait_impls = Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum)); @@ -298,6 +319,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { && module_id.is_local() { if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() { + self.local_doc_reachable.insert(def_id); let scope_id = match child.res { Res::Def( DefKind::Variant diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index fd4f9254107ca..07d8b78d767db 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -1,7 +1,8 @@ use crate::core::DocContext; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::TyCtxt; +use rustc_resolve::Resolver; // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses @@ -25,6 +26,10 @@ impl RustdocEffectiveVisibilities { define_method!(is_directly_public); define_method!(is_exported); define_method!(is_reachable); + + pub(crate) fn init(&mut self, extern_public: DefIdSet) { + self.extern_public = extern_public; + } } pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { @@ -37,6 +42,17 @@ pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { .visit_item(def_id) } +pub(crate) fn early_lib_embargo_visit_item( + resolver: &Resolver<'_>, + extern_public: &mut DefIdSet, + def_id: DefId, + is_mod: bool, +) { + assert!(!def_id.is_local()); + EarlyLibEmbargoVisitor { resolver, extern_public, visited_mods: Default::default() } + .visit_item(def_id, is_mod) +} + /// Similar to `librustc_privacy::EmbargoVisitor`, but also takes /// specific rustdoc annotations into account (i.e., `doc(hidden)`) struct LibEmbargoVisitor<'a, 'tcx> { @@ -47,6 +63,14 @@ struct LibEmbargoVisitor<'a, 'tcx> { visited_mods: DefIdSet, } +struct EarlyLibEmbargoVisitor<'r, 'ra> { + resolver: &'r Resolver<'ra>, + // Effective visibilities for reachable nodes + extern_public: &'r mut DefIdSet, + // Keeps track of already visited modules, in case a module re-exports its parent + visited_mods: DefIdSet, +} + impl LibEmbargoVisitor<'_, '_> { fn visit_mod(&mut self, def_id: DefId) { if !self.visited_mods.insert(def_id) { @@ -71,3 +95,28 @@ impl LibEmbargoVisitor<'_, '_> { } } } + +impl EarlyLibEmbargoVisitor<'_, '_> { + fn visit_mod(&mut self, def_id: DefId) { + if !self.visited_mods.insert(def_id) { + return; + } + + for item in self.resolver.cstore().module_children_untracked(def_id, self.resolver.sess()) { + if let Some(def_id) = item.res.opt_def_id() { + if item.vis.is_public() { + self.visit_item(def_id, matches!(item.res, Res::Def(DefKind::Mod, _))); + } + } + } + } + + fn visit_item(&mut self, def_id: DefId, is_mod: bool) { + if !self.resolver.cstore().is_doc_hidden_untracked(def_id) { + self.extern_public.insert(def_id); + if is_mod { + self.visit_mod(def_id); + } + } + } +} From ca93310eb7b18eb34ca743fad530ae4a0e34c5c7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 25 Jan 2023 23:22:00 +0400 Subject: [PATCH 2/3] rustdoc: Use rustdoc-reachable set to prune extern impls --- .../passes/collect_intra_doc_links/early.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 150e53f63b9a5..920028dd63c8f 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -118,6 +118,10 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { } } + fn is_doc_reachable(&self, def_id: DefId) -> bool { + self.extern_doc_reachable.contains(&def_id) || self.local_doc_reachable.contains(&def_id) + } + /// 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 @@ -148,10 +152,10 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { // 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.is_doc_reachable(trait_def_id) + && simplified_self_ty + .and_then(|ty| ty.def()) + .map_or(true, |ty_def_id| self.is_doc_reachable(ty_def_id)) { if self.visited_mods.insert(trait_def_id) { self.resolve_doc_links_extern_impl(trait_def_id, false); @@ -160,7 +164,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { } } for (ty_def_id, impl_def_id) in all_inherent_impls { - if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() { + if self.is_doc_reachable(ty_def_id) { self.resolve_doc_links_extern_impl(impl_def_id, true); } } From 398225842cbff0cdd45021bfe91712d407a900b8 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 25 Jan 2023 23:29:19 +0400 Subject: [PATCH 3/3] rustdoc: Don't put non rustdoc-reachable impls into `all_trait_impls` --- src/librustdoc/passes/collect_intra_doc_links/early.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 920028dd63c8f..f690c49005d9c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -162,6 +162,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { } self.resolve_doc_links_extern_impl(impl_def_id, false); } + self.all_trait_impls.push(impl_def_id); } for (ty_def_id, impl_def_id) in all_inherent_impls { if self.is_doc_reachable(ty_def_id) { @@ -171,9 +172,6 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { 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 {