1 //! A collection of utility functions for the `strip_*` passes.
2 use rustc_hir::def_id::DefId;
3 use rustc_middle::middle::privacy::AccessLevels;
6 use crate::clean::{self, Item, ItemId, ItemIdSet};
7 use crate::fold::{strip_item, DocFolder};
8 use crate::formats::cache::Cache;
10 pub(crate) struct Stripper<'a> {
11 pub(crate) retained: &'a mut ItemIdSet,
12 pub(crate) access_levels: &'a AccessLevels<DefId>,
13 pub(crate) update_retained: bool,
14 pub(crate) is_json_output: bool,
17 // We need to handle this differently for the JSON output because some non exported items could
18 // be used in public API. And so, we need these items as well. `is_exported` only checks if they
19 // are in the public API, which is not enough.
23 access_levels: &AccessLevels<DefId>,
27 access_levels.is_reachable(item_id.expect_def_id())
29 access_levels.is_exported(item_id.expect_def_id())
33 impl<'a> DocFolder for Stripper<'a> {
34 fn fold_item(&mut self, i: Item) -> Option<Item> {
36 clean::StrippedItem(..) => {
37 // We need to recurse into stripped modules to strip things
38 // like impl methods but when doing so we must not add any
39 // items to the `retained` set.
40 debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
41 let old = mem::replace(&mut self.update_retained, false);
42 let ret = self.fold_item_recur(i);
43 self.update_retained = old;
46 // These items can all get re-exported
47 clean::OpaqueTyItem(..)
48 | clean::TypedefItem(..)
49 | clean::StaticItem(..)
50 | clean::StructItem(..)
52 | clean::TraitItem(..)
53 | clean::FunctionItem(..)
54 | clean::VariantItem(..)
55 | clean::MethodItem(..)
56 | clean::ForeignFunctionItem(..)
57 | clean::ForeignStaticItem(..)
58 | clean::ConstantItem(..)
59 | clean::UnionItem(..)
60 | clean::AssocConstItem(..)
61 | clean::AssocTypeItem(..)
62 | clean::TraitAliasItem(..)
63 | clean::MacroItem(..)
64 | clean::ForeignTypeItem => {
65 let item_id = i.item_id;
67 && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
69 debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
74 clean::StructFieldItem(..) => {
75 if !i.visibility.is_public() {
76 return Some(strip_item(i));
80 clean::ModuleItem(..) => {
81 if i.item_id.is_local() && !i.visibility.is_public() {
82 debug!("Stripper: stripping module {:?}", i.name);
83 let old = mem::replace(&mut self.update_retained, false);
84 let ret = strip_item(self.fold_item_recur(i));
85 self.update_retained = old;
90 // handled in the `strip-priv-imports` pass
91 clean::ExternCrateItem { .. } => {}
92 clean::ImportItem(ref imp) => {
93 // Because json doesn't inline imports from private modules, we need to mark
94 // the imported item as retained so it's impls won't be stripped.
96 // FIXME: Is it necessary to check for json output here: See
97 // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
98 if let Some(did) = imp.source.did && self.is_json_output {
99 self.retained.insert(did.into());
103 clean::ImplItem(..) => {}
105 // tymethods etc. have no control over privacy
106 clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
108 // Proc-macros are always public
109 clean::ProcMacroItem(..) => {}
111 // Primitives are never stripped
112 clean::PrimitiveItem(..) => {}
114 // Keywords are never stripped
115 clean::KeywordItem => {}
118 let fastreturn = match *i.kind {
119 // nothing left to do for traits (don't want to filter their
120 // methods out, visibility controlled by the trait)
121 clean::TraitItem(..) => true,
123 // implementations of traits are always public.
124 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
125 // Variant fields have inherited visibility
126 clean::VariantItem(clean::Variant::Struct(..) | clean::Variant::Tuple(..)) => true,
130 let i = if fastreturn {
131 if self.update_retained {
132 self.retained.insert(i.item_id);
136 self.fold_item_recur(i)
139 if self.update_retained {
140 self.retained.insert(i.item_id);
146 /// This stripper discards all impls which reference stripped items
147 pub(crate) struct ImplStripper<'a> {
148 pub(crate) retained: &'a ItemIdSet,
149 pub(crate) cache: &'a Cache,
150 pub(crate) is_json_output: bool,
151 pub(crate) document_private: bool,
154 impl<'a> DocFolder for ImplStripper<'a> {
155 fn fold_item(&mut self, i: Item) -> Option<Item> {
156 if let clean::ImplItem(ref imp) = *i.kind {
157 // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
160 // There is one special case: if the impl block contains only private items.
161 if imp.trait_.is_none() {
162 // If the only items present are private ones and we're not rendering private items,
163 // we don't document it.
164 if !imp.items.is_empty()
165 && !self.document_private
166 && imp.items.iter().all(|i| {
167 let item_id = i.item_id;
169 && !is_item_reachable(
171 &self.cache.access_levels,
177 } else if imp.items.is_empty() && i.doc_value().is_none() {
181 if let Some(did) = imp.for_.def_id(self.cache) {
182 if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
184 debug!("ImplStripper: impl item for stripped type; removing");
188 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
189 if did.is_local() && !self.retained.contains(&did.into()) {
190 debug!("ImplStripper: impl item for stripped trait; removing");
194 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
195 for typaram in generics {
196 if let Some(did) = typaram.def_id(self.cache) {
197 if did.is_local() && !self.retained.contains(&did.into()) {
199 "ImplStripper: stripped item in trait's generics; removing impl"
207 Some(self.fold_item_recur(i))
211 /// This stripper discards all private import statements (`use`, `extern crate`)
212 pub(crate) struct ImportStripper;
214 impl DocFolder for ImportStripper {
215 fn fold_item(&mut self, i: Item) -> Option<Item> {
217 clean::ExternCrateItem { .. } | clean::ImportItem(..) if !i.visibility.is_public() => {
220 _ => Some(self.fold_item_recur(i)),