]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/stripper.rs
Rollup merge of #103719 - joseluis:fix-typos-try-reserve, r=the8472
[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::EffectiveVisibilities;
4 use rustc_span::symbol::sym;
5
6 use std::mem;
7
8 use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
9 use crate::fold::{strip_item, DocFolder};
10 use crate::formats::cache::Cache;
11
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,
17 }
18
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.
22 #[inline]
23 fn is_item_reachable(
24     is_json_output: bool,
25     effective_visibilities: &EffectiveVisibilities<DefId>,
26     item_id: ItemId,
27 ) -> bool {
28     if is_json_output {
29         effective_visibilities.is_reachable(item_id.expect_def_id())
30     } else {
31         effective_visibilities.is_exported(item_id.expect_def_id())
32     }
33 }
34
35 impl<'a> DocFolder for Stripper<'a> {
36     fn fold_item(&mut self, i: Item) -> Option<Item> {
37         match *i.kind {
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;
46                 return Some(ret);
47             }
48             // These items can all get re-exported
49             clean::OpaqueTyItem(..)
50             | clean::TypedefItem(..)
51             | clean::StaticItem(..)
52             | clean::StructItem(..)
53             | clean::EnumItem(..)
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;
68                 if item_id.is_local()
69                     && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id)
70                 {
71                     debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
72                     return None;
73                 }
74             }
75
76             clean::StructFieldItem(..) => {
77                 if !i.visibility.is_public() {
78                     return Some(strip_item(i));
79                 }
80             }
81
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;
88                     return Some(ret);
89                 }
90             }
91
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.
97                 //
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());
102                 }
103             }
104
105             clean::ImplItem(..) => {}
106
107             // tymethods etc. have no control over privacy
108             clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
109
110             // Proc-macros are always public
111             clean::ProcMacroItem(..) => {}
112
113             // Primitives are never stripped
114             clean::PrimitiveItem(..) => {}
115
116             // Keywords are never stripped
117             clean::KeywordItem => {}
118         }
119
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,
124
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,
129             _ => false,
130         };
131
132         let i = if fastreturn {
133             if self.update_retained {
134                 self.retained.insert(i.item_id);
135             }
136             return Some(i);
137         } else {
138             self.fold_item_recur(i)
139         };
140
141         if self.update_retained {
142             self.retained.insert(i.item_id);
143         }
144         Some(i)
145     }
146 }
147
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,
154 }
155
156 impl<'a> ImplStripper<'a> {
157     #[inline]
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()) {
160             true
161         } else if self.is_json_output {
162             // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
163             // need to keep it.
164             self.cache.effective_visibilities.is_exported(for_def_id)
165                 && !item.attrs.lists(sym::doc).has_word(sym::hidden)
166         } else {
167             false
168         }
169     }
170 }
171
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
176             // documentation.
177             //
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;
186                         item_id.is_local()
187                             && !is_item_reachable(
188                                 self.is_json_output,
189                                 &self.cache.effective_visibilities,
190                                 item_id,
191                             )
192                     })
193                 {
194                     return None;
195                 } else if imp.items.is_empty() && i.doc_value().is_none() {
196                     return None;
197                 }
198             }
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");
205                     return None;
206                 }
207             }
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");
211                     return None;
212                 }
213             }
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) {
218                             debug!(
219                                 "ImplStripper: stripped item in trait's generics; removing impl"
220                             );
221                             return None;
222                         }
223                     }
224                 }
225             }
226         }
227         Some(self.fold_item_recur(i))
228     }
229 }
230
231 /// This stripper discards all private import statements (`use`, `extern crate`)
232 pub(crate) struct ImportStripper;
233
234 impl DocFolder for ImportStripper {
235     fn fold_item(&mut self, i: Item) -> Option<Item> {
236         match *i.kind {
237             clean::ExternCrateItem { .. } | clean::ImportItem(..) if !i.visibility.is_public() => {
238                 None
239             }
240             _ => Some(self.fold_item_recur(i)),
241         }
242     }
243 }