diff options
Diffstat (limited to '')
-rw-r--r-- | src/librustdoc/passes/stripper.rs | 99 |
1 files changed, 77 insertions, 22 deletions
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 0d419042a..0089ce63d 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -1,30 +1,34 @@ //! A collection of utility functions for the `strip_*` passes. use rustc_hir::def_id::DefId; -use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::privacy::EffectiveVisibilities; +use rustc_span::symbol::sym; + use std::mem; -use crate::clean::{self, Item, ItemId, ItemIdSet}; +use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt}; use crate::fold::{strip_item, DocFolder}; use crate::formats::cache::Cache; pub(crate) struct Stripper<'a> { pub(crate) retained: &'a mut ItemIdSet, - pub(crate) access_levels: &'a AccessLevels<DefId>, + pub(crate) effective_visibilities: &'a EffectiveVisibilities<DefId>, pub(crate) update_retained: bool, pub(crate) is_json_output: bool, } -impl<'a> Stripper<'a> { - // We need to handle this differently for the JSON output because some non exported items could - // be used in public API. And so, we need these items as well. `is_exported` only checks if they - // are in the public API, which is not enough. - #[inline] - fn is_item_reachable(&self, item_id: ItemId) -> bool { - if self.is_json_output { - self.access_levels.is_reachable(item_id.expect_def_id()) - } else { - self.access_levels.is_exported(item_id.expect_def_id()) - } +// We need to handle this differently for the JSON output because some non exported items could +// be used in public API. And so, we need these items as well. `is_exported` only checks if they +// are in the public API, which is not enough. +#[inline] +fn is_item_reachable( + is_json_output: bool, + effective_visibilities: &EffectiveVisibilities<DefId>, + item_id: ItemId, +) -> bool { + if is_json_output { + effective_visibilities.is_reachable(item_id.expect_def_id()) + } else { + effective_visibilities.is_exported(item_id.expect_def_id()) } } @@ -61,7 +65,9 @@ impl<'a> DocFolder for Stripper<'a> { | clean::MacroItem(..) | clean::ForeignTypeItem => { let item_id = i.item_id; - if item_id.is_local() && !self.is_item_reachable(item_id) { + if item_id.is_local() + && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id) + { debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); return None; } @@ -84,7 +90,17 @@ impl<'a> DocFolder for Stripper<'a> { } // handled in the `strip-priv-imports` pass - clean::ExternCrateItem { .. } | clean::ImportItem(..) => {} + clean::ExternCrateItem { .. } => {} + clean::ImportItem(ref imp) => { + // Because json doesn't inline imports from private modules, we need to mark + // the imported item as retained so it's impls won't be stripped. + // + // FIXME: Is it necessary to check for json output here: See + // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215 + if let Some(did) = imp.source.did && self.is_json_output { + self.retained.insert(did.into()); + } + } clean::ImplItem(..) => {} @@ -133,6 +149,24 @@ impl<'a> DocFolder for Stripper<'a> { pub(crate) struct ImplStripper<'a> { 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> { + #[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()) { + true + } 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) + && !item.attrs.lists(sym::doc).has_word(sym::hidden) + } else { + false + } + } } impl<'a> DocFolder for ImplStripper<'a> { @@ -140,18 +174,39 @@ impl<'a> DocFolder for ImplStripper<'a> { if let clean::ImplItem(ref imp) = *i.kind { // Impl blocks can be skipped if they are: empty; not a trait impl; and have no // documentation. - if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() { - return None; + // + // There is one special case: if the impl block contains only private items. + if imp.trait_.is_none() { + // If the only items present are private ones and we're not rendering private items, + // we don't document it. + if !imp.items.is_empty() + && !self.document_private + && imp.items.iter().all(|i| { + let item_id = i.item_id; + item_id.is_local() + && !is_item_reachable( + self.is_json_output, + &self.cache.effective_visibilities, + item_id, + ) + }) + { + return None; + } else if imp.items.is_empty() && i.doc_value().is_none() { + return None; + } } + // Because we don't inline in `maybe_inline_local` if the output format is JSON, + // we need to make a special check for JSON output: we want to keep it unless it has + // a `#[doc(hidden)]` attribute if the `for_` type is exported. if let Some(did) = imp.for_.def_id(self.cache) { - if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into()) - { + if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) { debug!("ImplStripper: impl item for stripped type; removing"); return None; } } if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) { - if did.is_local() && !self.retained.contains(&did.into()) { + if !self.should_keep_impl(&i, did) { debug!("ImplStripper: impl item for stripped trait; removing"); return None; } @@ -159,7 +214,7 @@ impl<'a> DocFolder for ImplStripper<'a> { if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { for typaram in generics { if let Some(did) = typaram.def_id(self.cache) { - if did.is_local() && !self.retained.contains(&did.into()) { + if !self.should_keep_impl(&i, did) { debug!( "ImplStripper: stripped item in trait's generics; removing impl" ); |