]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/stripper.rs
fix various subst_identity vs skip_binder
[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::ty::{TyCtxt, Visibility};
4 use rustc_span::symbol::sym;
5 use std::mem;
6
7 use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
8 use crate::fold::{strip_item, DocFolder};
9 use crate::formats::cache::Cache;
10 use crate::visit_lib::RustdocEffectiveVisibilities;
11
12 pub(crate) struct Stripper<'a, 'tcx> {
13     pub(crate) retained: &'a mut ItemIdSet,
14     pub(crate) effective_visibilities: &'a RustdocEffectiveVisibilities,
15     pub(crate) update_retained: bool,
16     pub(crate) is_json_output: bool,
17     pub(crate) tcx: TyCtxt<'tcx>,
18 }
19
20 // We need to handle this differently for the JSON output because some non exported items could
21 // be used in public API. And so, we need these items as well. `is_exported` only checks if they
22 // are in the public API, which is not enough.
23 #[inline]
24 fn is_item_reachable(
25     tcx: TyCtxt<'_>,
26     is_json_output: bool,
27     effective_visibilities: &RustdocEffectiveVisibilities,
28     item_id: ItemId,
29 ) -> bool {
30     if is_json_output {
31         effective_visibilities.is_reachable(tcx, item_id.expect_def_id())
32     } else {
33         effective_visibilities.is_exported(tcx, item_id.expect_def_id())
34     }
35 }
36
37 impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
38     fn fold_item(&mut self, i: Item) -> Option<Item> {
39         match *i.kind {
40             clean::StrippedItem(..) => {
41                 // We need to recurse into stripped modules to strip things
42                 // like impl methods but when doing so we must not add any
43                 // items to the `retained` set.
44                 debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
45                 let old = mem::replace(&mut self.update_retained, false);
46                 let ret = self.fold_item_recur(i);
47                 self.update_retained = old;
48                 return Some(ret);
49             }
50             // These items can all get re-exported
51             clean::OpaqueTyItem(..)
52             | clean::TypedefItem(..)
53             | clean::StaticItem(..)
54             | clean::StructItem(..)
55             | clean::EnumItem(..)
56             | clean::TraitItem(..)
57             | clean::FunctionItem(..)
58             | clean::VariantItem(..)
59             | clean::MethodItem(..)
60             | clean::ForeignFunctionItem(..)
61             | clean::ForeignStaticItem(..)
62             | clean::ConstantItem(..)
63             | clean::UnionItem(..)
64             | clean::AssocConstItem(..)
65             | clean::AssocTypeItem(..)
66             | clean::TraitAliasItem(..)
67             | clean::MacroItem(..)
68             | clean::ForeignTypeItem => {
69                 let item_id = i.item_id;
70                 if item_id.is_local()
71                     && !is_item_reachable(
72                         self.tcx,
73                         self.is_json_output,
74                         self.effective_visibilities,
75                         item_id,
76                     )
77                 {
78                     debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
79                     return None;
80                 }
81             }
82
83             clean::StructFieldItem(..) => {
84                 if i.visibility(self.tcx) != Some(Visibility::Public) {
85                     return Some(strip_item(i));
86                 }
87             }
88
89             clean::ModuleItem(..) => {
90                 if i.item_id.is_local() && i.visibility(self.tcx) != Some(Visibility::Public) {
91                     debug!("Stripper: stripping module {:?}", i.name);
92                     let old = mem::replace(&mut self.update_retained, false);
93                     let ret = strip_item(self.fold_item_recur(i));
94                     self.update_retained = old;
95                     return Some(ret);
96                 }
97             }
98
99             // handled in the `strip-priv-imports` pass
100             clean::ExternCrateItem { .. } => {}
101             clean::ImportItem(ref imp) => {
102                 // Because json doesn't inline imports from private modules, we need to mark
103                 // the imported item as retained so it's impls won't be stripped.
104                 //
105                 // FIXME: Is it necessary to check for json output here: See
106                 // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
107                 if let Some(did) = imp.source.did && self.is_json_output {
108                     self.retained.insert(did.into());
109                 }
110             }
111
112             clean::ImplItem(..) => {}
113
114             // tymethods etc. have no control over privacy
115             clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
116
117             // Proc-macros are always public
118             clean::ProcMacroItem(..) => {}
119
120             // Primitives are never stripped
121             clean::PrimitiveItem(..) => {}
122
123             // Keywords are never stripped
124             clean::KeywordItem => {}
125         }
126
127         let fastreturn = match *i.kind {
128             // nothing left to do for traits (don't want to filter their
129             // methods out, visibility controlled by the trait)
130             clean::TraitItem(..) => true,
131
132             // implementations of traits are always public.
133             clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
134             // Variant fields have inherited visibility
135             clean::VariantItem(clean::Variant {
136                 kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
137                 ..
138             }) => true,
139             _ => false,
140         };
141
142         let i = if fastreturn {
143             if self.update_retained {
144                 self.retained.insert(i.item_id);
145             }
146             return Some(i);
147         } else {
148             self.fold_item_recur(i)
149         };
150
151         if self.update_retained {
152             self.retained.insert(i.item_id);
153         }
154         Some(i)
155     }
156 }
157
158 /// This stripper discards all impls which reference stripped items
159 pub(crate) struct ImplStripper<'a, 'tcx> {
160     pub(crate) tcx: TyCtxt<'tcx>,
161     pub(crate) retained: &'a ItemIdSet,
162     pub(crate) cache: &'a Cache,
163     pub(crate) is_json_output: bool,
164     pub(crate) document_private: bool,
165 }
166
167 impl<'a> ImplStripper<'a, '_> {
168     #[inline]
169     fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
170         if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
171             true
172         } else if self.is_json_output {
173             // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
174             // need to keep it.
175             self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
176                 && !item.attrs.lists(sym::doc).has_word(sym::hidden)
177         } else {
178             false
179         }
180     }
181 }
182
183 impl<'a> DocFolder for ImplStripper<'a, '_> {
184     fn fold_item(&mut self, i: Item) -> Option<Item> {
185         if let clean::ImplItem(ref imp) = *i.kind {
186             // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
187             // documentation.
188             //
189             // There is one special case: if the impl block contains only private items.
190             if imp.trait_.is_none() {
191                 // If the only items present are private ones and we're not rendering private items,
192                 // we don't document it.
193                 if !imp.items.is_empty()
194                     && !self.document_private
195                     && imp.items.iter().all(|i| {
196                         let item_id = i.item_id;
197                         item_id.is_local()
198                             && !is_item_reachable(
199                                 self.tcx,
200                                 self.is_json_output,
201                                 &self.cache.effective_visibilities,
202                                 item_id,
203                             )
204                     })
205                 {
206                     return None;
207                 } else if imp.items.is_empty() && i.doc_value().is_none() {
208                     return None;
209                 }
210             }
211             // Because we don't inline in `maybe_inline_local` if the output format is JSON,
212             // we need to make a special check for JSON output: we want to keep it unless it has
213             // a `#[doc(hidden)]` attribute if the `for_` type is exported.
214             if let Some(did) = imp.for_.def_id(self.cache) {
215                 if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) {
216                     debug!("ImplStripper: impl item for stripped type; removing");
217                     return None;
218                 }
219             }
220             if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
221                 if !self.should_keep_impl(&i, did) {
222                     debug!("ImplStripper: impl item for stripped trait; removing");
223                     return None;
224                 }
225             }
226             if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
227                 for typaram in generics {
228                     if let Some(did) = typaram.def_id(self.cache) {
229                         if !self.should_keep_impl(&i, did) {
230                             debug!(
231                                 "ImplStripper: stripped item in trait's generics; removing impl"
232                             );
233                             return None;
234                         }
235                     }
236                 }
237             }
238         }
239         Some(self.fold_item_recur(i))
240     }
241 }
242
243 /// This stripper discards all private import statements (`use`, `extern crate`)
244 pub(crate) struct ImportStripper<'tcx> {
245     pub(crate) tcx: TyCtxt<'tcx>,
246     pub(crate) is_json_output: bool,
247 }
248
249 impl<'tcx> ImportStripper<'tcx> {
250     fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
251         if self.is_json_output {
252             // FIXME: This should be handled the same way as for HTML output.
253             imp.imported_item_is_doc_hidden(self.tcx)
254         } else {
255             i.attrs.lists(sym::doc).has_word(sym::hidden)
256         }
257     }
258 }
259
260 impl<'tcx> DocFolder for ImportStripper<'tcx> {
261     fn fold_item(&mut self, i: Item) -> Option<Item> {
262         match *i.kind {
263             clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None,
264             clean::ImportItem(_) if i.attrs.lists(sym::doc).has_word(sym::hidden) => None,
265             clean::ExternCrateItem { .. } | clean::ImportItem(..)
266                 if i.visibility(self.tcx) != Some(Visibility::Public) =>
267             {
268                 None
269             }
270             _ => Some(self.fold_item_recur(i)),
271         }
272     }
273 }