From 90f27f93bdb9e3033faf87ad0b813d3faf077285 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 19 Oct 2022 22:37:59 +0400 Subject: [PATCH] rustdoc: Split effective visibilities from rustc from similar data built by rustdoc for external def-ids --- src/librustdoc/clean/blanket_impl.rs | 2 +- src/librustdoc/clean/inline.rs | 4 +- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/core.rs | 3 +- src/librustdoc/formats/cache.rs | 13 +++---- src/librustdoc/html/format.rs | 2 +- .../passes/check_doc_test_visibility.rs | 4 +- src/librustdoc/passes/strip_hidden.rs | 1 + src/librustdoc/passes/strip_private.rs | 2 + src/librustdoc/passes/stripper.rs | 34 ++++++++++------ src/librustdoc/visit_ast.rs | 3 +- src/librustdoc/visit_lib.rs | 39 +++++++++++++------ 12 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 8b63c3db3c3..f82fb498131 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -20,7 +20,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); for trait_def_id in cx.tcx.all_traits() { - if !cx.cache.effective_visibilities.is_directly_public(trait_def_id) + if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, trait_def_id) || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some() { continue; diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index b9f787729c1..c823ed0f16e 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -374,7 +374,7 @@ pub(crate) fn build_impl( if !did.is_local() { if let Some(traitref) = associated_trait { let did = traitref.def_id; - if !cx.cache.effective_visibilities.is_directly_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(tcx, did) { return; } @@ -403,7 +403,7 @@ pub(crate) fn build_impl( // reachable in rustdoc generated documentation if !did.is_local() { if let Some(did) = for_.def_id(&cx.cache) { - if !cx.cache.effective_visibilities.is_directly_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(tcx, did) { return; } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 64a18757b26..8a0e6a82126 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1431,7 +1431,7 @@ fn maybe_expand_private_type_alias<'tcx>( let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; - let alias = if !cx.cache.effective_visibilities.is_exported(def_id.to_def_id()) { + let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) { &cx.tcx.hir().expect_item(def_id).kind } else { return None; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 3e5f42b7a80..3961802529b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -348,7 +348,6 @@ pub(crate) fn run_global_ctxt( let auto_traits = tcx.all_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect(); - let effective_visibilities = tcx.effective_visibilities(()).map_id(Into::into); let mut ctxt = DocContext { tcx, @@ -361,7 +360,7 @@ pub(crate) fn run_global_ctxt( impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), auto_traits, - cache: Cache::new(effective_visibilities, render_options.document_private), + cache: Cache::new(render_options.document_private), inlined: FxHashSet::default(), output_format, render_options, diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index afe2264e8bf..a0cf1ec78e2 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -2,7 +2,6 @@ use std::mem; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; @@ -15,6 +14,7 @@ use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::search_index::get_function_type_for_search; use crate::html::render::IndexItem; +use crate::visit_lib::RustdocEffectiveVisibilities; /// This cache is used to store information about the [`clean::Crate`] being /// rendered in order to provide more useful documentation. This contains @@ -78,7 +78,7 @@ pub(crate) struct Cache { // 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 effective visibilities from the privacy check pass. - pub(crate) effective_visibilities: EffectiveVisibilities, + pub(crate) effective_visibilities: RustdocEffectiveVisibilities, /// The version of the crate being documented, if given from the `--crate-version` flag. pub(crate) crate_version: Option, @@ -132,11 +132,8 @@ struct CacheBuilder<'a, 'tcx> { } impl Cache { - pub(crate) fn new( - effective_visibilities: EffectiveVisibilities, - document_private: bool, - ) -> Self { - Cache { effective_visibilities, document_private, ..Cache::default() } + pub(crate) fn new(document_private: bool) -> Self { + Cache { document_private, ..Cache::default() } } /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was @@ -387,7 +384,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { || self .cache .effective_visibilities - .is_directly_public(item.item_id.expect_def_id()) + .is_directly_public(self.tcx, item.item_id.expect_def_id()) { self.cache.paths.insert( item.item_id.expect_def_id(), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 92e7f2739af..37202f786ed 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -659,7 +659,7 @@ pub(crate) fn href_with_root_path( } if !did.is_local() - && !cache.effective_visibilities.is_directly_public(did) + && !cache.effective_visibilities.is_directly_public(tcx, did) && !cache.document_private && !cache.primitive_locations.values().any(|&id| id == did) { diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 7740c6d5bbb..057d2fdd9d5 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -56,7 +56,7 @@ impl crate::doctest::Tester for Tests { } pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if !cx.cache.effective_visibilities.is_directly_public(item.item_id.expect_def_id()) + if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item.item_id.expect_def_id()) || matches!( *item.kind, clean::StructFieldItem(_) @@ -130,7 +130,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item ); } } else if tests.found_tests > 0 - && !cx.cache.effective_visibilities.is_exported(item.item_id.expect_def_id()) + && !cx.cache.effective_visibilities.is_exported(cx.tcx, item.item_id.expect_def_id()) { cx.tcx.struct_span_lint_hir( crate::lint::PRIVATE_DOC_TESTS, diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 9914edf3036..e07a788a72a 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -27,6 +27,7 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea // strip all impls referencing stripped items let mut stripper = ImplStripper { + tcx: cx.tcx, retained: &retained, cache: &cx.cache, is_json_output, diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 450f69e15d1..e3b958b2036 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -22,6 +22,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> // strip all private items { let mut stripper = Stripper { + tcx: cx.tcx, retained: &mut retained, effective_visibilities: &cx.cache.effective_visibilities, update_retained: true, @@ -32,6 +33,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> // strip all impls referencing private items let mut stripper = ImplStripper { + tcx: cx.tcx, retained: &retained, cache: &cx.cache, is_json_output, diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 0089ce63d07..4fa5c04ddf6 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -1,6 +1,6 @@ //! A collection of utility functions for the `strip_*` passes. use rustc_hir::def_id::DefId; -use rustc_middle::middle::privacy::EffectiveVisibilities; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use std::mem; @@ -8,10 +8,12 @@ use std::mem; use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt}; use crate::fold::{strip_item, DocFolder}; use crate::formats::cache::Cache; +use crate::visit_lib::RustdocEffectiveVisibilities; -pub(crate) struct Stripper<'a> { +pub(crate) struct Stripper<'a, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, pub(crate) retained: &'a mut ItemIdSet, - pub(crate) effective_visibilities: &'a EffectiveVisibilities, + pub(crate) effective_visibilities: &'a RustdocEffectiveVisibilities, pub(crate) update_retained: bool, pub(crate) is_json_output: bool, } @@ -21,18 +23,19 @@ pub(crate) struct Stripper<'a> { // are in the public API, which is not enough. #[inline] fn is_item_reachable( + tcx: TyCtxt<'_>, is_json_output: bool, - effective_visibilities: &EffectiveVisibilities, + effective_visibilities: &RustdocEffectiveVisibilities, item_id: ItemId, ) -> bool { if is_json_output { - effective_visibilities.is_reachable(item_id.expect_def_id()) + effective_visibilities.is_reachable(tcx, item_id.expect_def_id()) } else { - effective_visibilities.is_exported(item_id.expect_def_id()) + effective_visibilities.is_exported(tcx, item_id.expect_def_id()) } } -impl<'a> DocFolder for Stripper<'a> { +impl<'a> DocFolder for Stripper<'a, '_> { fn fold_item(&mut self, i: Item) -> Option { match *i.kind { clean::StrippedItem(..) => { @@ -66,7 +69,12 @@ impl<'a> DocFolder for Stripper<'a> { | clean::ForeignTypeItem => { let item_id = i.item_id; if item_id.is_local() - && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id) + && !is_item_reachable( + self.tcx, + self.is_json_output, + self.effective_visibilities, + item_id, + ) { debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); return None; @@ -146,14 +154,15 @@ impl<'a> DocFolder for Stripper<'a> { } /// This stripper discards all impls which reference stripped items -pub(crate) struct ImplStripper<'a> { +pub(crate) struct ImplStripper<'a, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, pub(crate) retained: &'a ItemIdSet, pub(crate) cache: &'a Cache, pub(crate) is_json_output: bool, pub(crate) document_private: bool, } -impl<'a> ImplStripper<'a> { +impl<'a> ImplStripper<'a, '_> { #[inline] fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool { if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) { @@ -161,7 +170,7 @@ impl<'a> ImplStripper<'a> { } else if self.is_json_output { // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we // need to keep it. - self.cache.effective_visibilities.is_exported(for_def_id) + self.cache.effective_visibilities.is_exported(self.tcx, for_def_id) && !item.attrs.lists(sym::doc).has_word(sym::hidden) } else { false @@ -169,7 +178,7 @@ impl<'a> ImplStripper<'a> { } } -impl<'a> DocFolder for ImplStripper<'a> { +impl<'a> DocFolder for ImplStripper<'a, '_> { fn fold_item(&mut self, i: Item) -> Option { if let clean::ImplItem(ref imp) = *i.kind { // Impl blocks can be skipped if they are: empty; not a trait impl; and have no @@ -185,6 +194,7 @@ impl<'a> DocFolder for ImplStripper<'a> { let item_id = i.item_id; item_id.is_local() && !is_item_reachable( + self.tcx, self.is_json_output, &self.cache.effective_visibilities, item_id, diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 7960ab251b7..7ee7eb25e0d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -229,7 +229,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { None => return false, }; - let is_private = !self.cx.cache.effective_visibilities.is_directly_public(res_did); + let is_private = + !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, res_did); let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id); // Only inline if requested or if the item would otherwise be stripped. diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 04a3114556e..e490559b0e9 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -1,18 +1,39 @@ use crate::core::DocContext; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; -use rustc_middle::ty::{TyCtxt, Visibility}; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses +#[derive(Default)] +pub(crate) struct RustdocEffectiveVisibilities { + extern_public: FxHashSet, +} + +macro_rules! define_method { + ($method:ident) => { + pub(crate) fn $method(&self, tcx: TyCtxt<'_>, def_id: DefId) -> bool { + match def_id.as_local() { + Some(def_id) => tcx.effective_visibilities(()).$method(def_id), + None => self.extern_public.contains(&def_id), + } + } + }; +} + +impl RustdocEffectiveVisibilities { + define_method!(is_directly_public); + define_method!(is_exported); + define_method!(is_reachable); +} + pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { assert!(!def_id.is_local()); LibEmbargoVisitor { tcx: cx.tcx, - effective_visibilities: &mut cx.cache.effective_visibilities, - visited_mods: FxHashSet::default(), + extern_public: &mut cx.cache.effective_visibilities.extern_public, + visited_mods: Default::default(), } .visit_item(def_id) } @@ -22,7 +43,7 @@ pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { struct LibEmbargoVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, // Effective visibilities for reachable nodes - effective_visibilities: &'a mut EffectiveVisibilities, + extern_public: &'a mut FxHashSet, // Keeps track of already visited modules, in case a module re-exports its parent visited_mods: FxHashSet, } @@ -44,11 +65,7 @@ impl LibEmbargoVisitor<'_, '_> { fn visit_item(&mut self, def_id: DefId) { if !self.tcx.is_doc_hidden(def_id) { - self.effective_visibilities.set_public_at_level( - def_id, - || Visibility::Restricted(CRATE_DEF_ID), - Level::Direct, - ); + self.extern_public.insert(def_id); if self.tcx.def_kind(def_id) == DefKind::Mod { self.visit_mod(def_id); }