use std::mem;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{TyCtxt, Visibility};
use tracing::debug;
use crate::clean::utils::inherits_doc_hidden;
use crate::clean::{self, Item, ItemId, ItemIdSet};
use crate::fold::{strip_item, DocFolder};
use crate::formats::cache::Cache;
use crate::visit_lib::RustdocEffectiveVisibilities;
pub(crate) struct Stripper<'a, 'tcx> {
pub(crate) retained: &'a mut ItemIdSet,
pub(crate) effective_visibilities: &'a RustdocEffectiveVisibilities,
pub(crate) update_retained: bool,
pub(crate) is_json_output: bool,
pub(crate) tcx: TyCtxt<'tcx>,
}
#[inline]
fn is_item_reachable(
tcx: TyCtxt<'_>,
is_json_output: bool,
effective_visibilities: &RustdocEffectiveVisibilities,
item_id: ItemId,
) -> bool {
if is_json_output {
effective_visibilities.is_reachable(tcx, item_id.expect_def_id())
} else {
effective_visibilities.is_exported(tcx, item_id.expect_def_id())
}
}
impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match *i.kind {
clean::StrippedItem(..) => {
debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = self.fold_item_recur(i);
self.update_retained = old;
return Some(ret);
}
clean::TypeAliasItem(..)
| clean::StaticItem(..)
| clean::StructItem(..)
| clean::EnumItem(..)
| clean::TraitItem(..)
| clean::FunctionItem(..)
| clean::VariantItem(..)
| clean::ForeignFunctionItem(..)
| clean::ForeignStaticItem(..)
| clean::ConstantItem(..)
| clean::UnionItem(..)
| clean::TraitAliasItem(..)
| clean::MacroItem(..)
| clean::ForeignTypeItem => {
let item_id = i.item_id;
if item_id.is_local()
&& !is_item_reachable(
self.tcx,
self.is_json_output,
self.effective_visibilities,
item_id,
)
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
}
}
clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
let item_id = i.item_id;
if item_id.is_local()
&& !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id())
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
}
}
clean::StructFieldItem(..) => {
if i.visibility(self.tcx) != Some(Visibility::Public) {
return Some(strip_item(i));
}
}
clean::ModuleItem(..) => {
if i.item_id.is_local()
&& !is_item_reachable(
self.tcx,
self.is_json_output,
self.effective_visibilities,
i.item_id,
)
{
debug!("Stripper: stripping module {:?}", i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = strip_item(self.fold_item_recur(i));
self.update_retained = old;
return Some(ret);
}
}
clean::ExternCrateItem { .. } | clean::ImportItem(_) => {}
clean::ImplItem(..) => {}
clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
clean::ProcMacroItem(..) => {}
clean::PrimitiveItem(..) => {}
clean::KeywordItem => {}
}
let fastreturn = match *i.kind {
clean::TraitItem(..) => true,
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
clean::VariantItem(clean::Variant {
kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
..
}) => true,
_ => false,
};
let i = if fastreturn {
if self.update_retained {
self.retained.insert(i.item_id);
}
return Some(i);
} else {
self.fold_item_recur(i)
};
if self.update_retained {
self.retained.insert(i.item_id);
}
Some(i)
}
}
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,
pub(crate) document_hidden: 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 {
self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
&& (self.document_hidden
|| ((!item.is_doc_hidden()
&& for_def_id
.as_local()
.map(|def_id| !inherits_doc_hidden(self.tcx, def_id, None))
.unwrap_or(true))
|| self.cache.inlined_items.contains(&for_def_id)))
} else {
false
}
}
}
impl<'a> DocFolder for ImplStripper<'a, '_> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = *i.kind {
if imp.trait_.is_none() {
if !imp.items.is_empty()
&& !self.document_private
&& imp.items.iter().all(|i| {
let item_id = i.item_id;
item_id.is_local()
&& !self
.cache
.effective_visibilities
.is_reachable(self.tcx, item_id.expect_def_id())
})
{
debug!("ImplStripper: no public item; removing {imp:?}");
return None;
} else if imp.items.is_empty() && i.doc_value().is_empty() {
debug!("ImplStripper: no item and no doc; removing {imp:?}");
return None;
}
}
if let Some(did) = imp.for_.def_id(self.cache)
&& !imp.for_.is_assoc_ty()
&& !self.should_keep_impl(&i, did)
{
debug!("ImplStripper: impl item for stripped type; removing {imp:?}");
return None;
}
if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id())
&& !self.should_keep_impl(&i, did)
{
debug!("ImplStripper: impl item for stripped trait; removing {imp:?}");
return None;
}
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)
&& !self.should_keep_impl(&i, did)
{
debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}");
return None;
}
}
}
}
Some(self.fold_item_recur(i))
}
}
pub(crate) struct ImportStripper<'tcx> {
pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) is_json_output: bool,
pub(crate) document_hidden: bool,
}
impl<'tcx> ImportStripper<'tcx> {
fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
if self.is_json_output {
imp.imported_item_is_doc_hidden(self.tcx)
} else {
i.is_doc_hidden()
}
}
}
impl<'tcx> DocFolder for ImportStripper<'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match *i.kind {
clean::ImportItem(imp)
if !self.document_hidden && self.import_should_be_hidden(&i, &imp) =>
{
None
}
clean::ExternCrateItem { .. } | clean::ImportItem(..)
if i.visibility(self.tcx) != Some(Visibility::Public) =>
{
None
}
_ => Some(self.fold_item_recur(i)),
}
}
}