]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/stripper.rs
Rollup merge of #101722 - aDotInTheVoid:rdy-ty-prim-docs, r=CraftSpider
[rust.git] / src / librustdoc / passes / stripper.rs
1 //! A collection of utility functions for the `strip_*` passes.
2 use rustc_hir::def_id::DefId;
3 use rustc_middle::middle::privacy::AccessLevels;
4 use std::mem;
5
6 use crate::clean::{self, Item, ItemId, ItemIdSet};
7 use crate::fold::{strip_item, DocFolder};
8 use crate::formats::cache::Cache;
9
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,
15 }
16
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.
20 #[inline]
21 fn is_item_reachable(
22     is_json_output: bool,
23     access_levels: &AccessLevels<DefId>,
24     item_id: ItemId,
25 ) -> bool {
26     if is_json_output {
27         access_levels.is_reachable(item_id.expect_def_id())
28     } else {
29         access_levels.is_exported(item_id.expect_def_id())
30     }
31 }
32
33 impl<'a> DocFolder for Stripper<'a> {
34     fn fold_item(&mut self, i: Item) -> Option<Item> {
35         match *i.kind {
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;
44                 return Some(ret);
45             }
46             // These items can all get re-exported
47             clean::OpaqueTyItem(..)
48             | clean::TypedefItem(..)
49             | clean::StaticItem(..)
50             | clean::StructItem(..)
51             | clean::EnumItem(..)
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;
66                 if item_id.is_local()
67                     && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
68                 {
69                     debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
70                     return None;
71                 }
72             }
73
74             clean::StructFieldItem(..) => {
75                 if !i.visibility.is_public() {
76                     return Some(strip_item(i));
77                 }
78             }
79
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;
86                     return Some(ret);
87                 }
88             }
89
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.
95                 //
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());
100                 }
101             }
102
103             clean::ImplItem(..) => {}
104
105             // tymethods etc. have no control over privacy
106             clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
107
108             // Proc-macros are always public
109             clean::ProcMacroItem(..) => {}
110
111             // Primitives are never stripped
112             clean::PrimitiveItem(..) => {}
113
114             // Keywords are never stripped
115             clean::KeywordItem => {}
116         }
117
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,
122
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,
127             _ => false,
128         };
129
130         let i = if fastreturn {
131             if self.update_retained {
132                 self.retained.insert(i.item_id);
133             }
134             return Some(i);
135         } else {
136             self.fold_item_recur(i)
137         };
138
139         if self.update_retained {
140             self.retained.insert(i.item_id);
141         }
142         Some(i)
143     }
144 }
145
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,
152 }
153
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
158             // documentation.
159             //
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;
168                         item_id.is_local()
169                             && !is_item_reachable(
170                                 self.is_json_output,
171                                 &self.cache.access_levels,
172                                 item_id,
173                             )
174                     })
175                 {
176                     return None;
177                 } else if imp.items.is_empty() && i.doc_value().is_none() {
178                     return None;
179                 }
180             }
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())
183                 {
184                     debug!("ImplStripper: impl item for stripped type; removing");
185                     return None;
186                 }
187             }
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");
191                     return None;
192                 }
193             }
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()) {
198                             debug!(
199                                 "ImplStripper: stripped item in trait's generics; removing impl"
200                             );
201                             return None;
202                         }
203                     }
204                 }
205             }
206         }
207         Some(self.fold_item_recur(i))
208     }
209 }
210
211 /// This stripper discards all private import statements (`use`, `extern crate`)
212 pub(crate) struct ImportStripper;
213
214 impl DocFolder for ImportStripper {
215     fn fold_item(&mut self, i: Item) -> Option<Item> {
216         match *i.kind {
217             clean::ExternCrateItem { .. } | clean::ImportItem(..) if !i.visibility.is_public() => {
218                 None
219             }
220             _ => Some(self.fold_item_recur(i)),
221         }
222     }
223 }