1 //! A collection of utility functions for the `strip_*` passes.
2 use rustc_hir::def_id::DefId;
3 use rustc_middle::middle::privacy::EffectiveVisibilities;
4 use rustc_span::symbol::sym;
8 use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
9 use crate::fold::{strip_item, DocFolder};
10 use crate::formats::cache::Cache;
12 pub(crate) struct Stripper<'a> {
13 pub(crate) retained: &'a mut ItemIdSet,
14 pub(crate) effective_visibilities: &'a EffectiveVisibilities<DefId>,
15 pub(crate) update_retained: bool,
16 pub(crate) is_json_output: bool,
19 // We need to handle this differently for the JSON output because some non exported items could
20 // be used in public API. And so, we need these items as well. `is_exported` only checks if they
21 // are in the public API, which is not enough.
25 effective_visibilities: &EffectiveVisibilities<DefId>,
29 effective_visibilities.is_reachable(item_id.expect_def_id())
31 effective_visibilities.is_exported(item_id.expect_def_id())
35 impl<'a> DocFolder for Stripper<'a> {
36 fn fold_item(&mut self, i: Item) -> Option<Item> {
38 clean::StrippedItem(..) => {
39 // We need to recurse into stripped modules to strip things
40 // like impl methods but when doing so we must not add any
41 // items to the `retained` set.
42 debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
43 let old = mem::replace(&mut self.update_retained, false);
44 let ret = self.fold_item_recur(i);
45 self.update_retained = old;
48 // These items can all get re-exported
49 clean::OpaqueTyItem(..)
50 | clean::TypedefItem(..)
51 | clean::StaticItem(..)
52 | clean::StructItem(..)
54 | clean::TraitItem(..)
55 | clean::FunctionItem(..)
56 | clean::VariantItem(..)
57 | clean::MethodItem(..)
58 | clean::ForeignFunctionItem(..)
59 | clean::ForeignStaticItem(..)
60 | clean::ConstantItem(..)
61 | clean::UnionItem(..)
62 | clean::AssocConstItem(..)
63 | clean::AssocTypeItem(..)
64 | clean::TraitAliasItem(..)
65 | clean::MacroItem(..)
66 | clean::ForeignTypeItem => {
67 let item_id = i.item_id;
69 && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id)
71 debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
76 clean::StructFieldItem(..) => {
77 if !i.visibility.is_public() {
78 return Some(strip_item(i));
82 clean::ModuleItem(..) => {
83 if i.item_id.is_local() && !i.visibility.is_public() {
84 debug!("Stripper: stripping module {:?}", i.name);
85 let old = mem::replace(&mut self.update_retained, false);
86 let ret = strip_item(self.fold_item_recur(i));
87 self.update_retained = old;
92 // handled in the `strip-priv-imports` pass
93 clean::ExternCrateItem { .. } => {}
94 clean::ImportItem(ref imp) => {
95 // Because json doesn't inline imports from private modules, we need to mark
96 // the imported item as retained so it's impls won't be stripped.
98 // FIXME: Is it necessary to check for json output here: See
99 // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
100 if let Some(did) = imp.source.did && self.is_json_output {
101 self.retained.insert(did.into());
105 clean::ImplItem(..) => {}
107 // tymethods etc. have no control over privacy
108 clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
110 // Proc-macros are always public
111 clean::ProcMacroItem(..) => {}
113 // Primitives are never stripped
114 clean::PrimitiveItem(..) => {}
116 // Keywords are never stripped
117 clean::KeywordItem => {}
120 let fastreturn = match *i.kind {
121 // nothing left to do for traits (don't want to filter their
122 // methods out, visibility controlled by the trait)
123 clean::TraitItem(..) => true,
125 // implementations of traits are always public.
126 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
127 // Variant fields have inherited visibility
128 clean::VariantItem(clean::Variant::Struct(..) | clean::Variant::Tuple(..)) => true,
132 let i = if fastreturn {
133 if self.update_retained {
134 self.retained.insert(i.item_id);
138 self.fold_item_recur(i)
141 if self.update_retained {
142 self.retained.insert(i.item_id);
148 /// This stripper discards all impls which reference stripped items
149 pub(crate) struct ImplStripper<'a> {
150 pub(crate) retained: &'a ItemIdSet,
151 pub(crate) cache: &'a Cache,
152 pub(crate) is_json_output: bool,
153 pub(crate) document_private: bool,
156 impl<'a> ImplStripper<'a> {
158 fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
159 if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
161 } else if self.is_json_output {
162 // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
164 self.cache.effective_visibilities.is_exported(for_def_id)
165 && !item.attrs.lists(sym::doc).has_word(sym::hidden)
172 impl<'a> DocFolder for ImplStripper<'a> {
173 fn fold_item(&mut self, i: Item) -> Option<Item> {
174 if let clean::ImplItem(ref imp) = *i.kind {
175 // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
178 // There is one special case: if the impl block contains only private items.
179 if imp.trait_.is_none() {
180 // If the only items present are private ones and we're not rendering private items,
181 // we don't document it.
182 if !imp.items.is_empty()
183 && !self.document_private
184 && imp.items.iter().all(|i| {
185 let item_id = i.item_id;
187 && !is_item_reachable(
189 &self.cache.effective_visibilities,
195 } else if imp.items.is_empty() && i.doc_value().is_none() {
199 // Because we don't inline in `maybe_inline_local` if the output format is JSON,
200 // we need to make a special check for JSON output: we want to keep it unless it has
201 // a `#[doc(hidden)]` attribute if the `for_` type is exported.
202 if let Some(did) = imp.for_.def_id(self.cache) {
203 if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) {
204 debug!("ImplStripper: impl item for stripped type; removing");
208 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
209 if !self.should_keep_impl(&i, did) {
210 debug!("ImplStripper: impl item for stripped trait; removing");
214 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
215 for typaram in generics {
216 if let Some(did) = typaram.def_id(self.cache) {
217 if !self.should_keep_impl(&i, did) {
219 "ImplStripper: stripped item in trait's generics; removing impl"
227 Some(self.fold_item_recur(i))
231 /// This stripper discards all private import statements (`use`, `extern crate`)
232 pub(crate) struct ImportStripper;
234 impl DocFolder for ImportStripper {
235 fn fold_item(&mut self, i: Item) -> Option<Item> {
237 clean::ExternCrateItem { .. } | clean::ImportItem(..) if !i.visibility.is_public() => {
240 _ => Some(self.fold_item_recur(i)),