]> git.proxmox.com Git - rustc.git/blob - src/librustdoc/passes/stripper.rs
New upstream version 1.69.0+dfsg1
[rustc.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 { .. } | clean::ImportItem(_) => {}
101
102 clean::ImplItem(..) => {}
103
104 // tymethods etc. have no control over privacy
105 clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
106
107 // Proc-macros are always public
108 clean::ProcMacroItem(..) => {}
109
110 // Primitives are never stripped
111 clean::PrimitiveItem(..) => {}
112
113 // Keywords are never stripped
114 clean::KeywordItem => {}
115 }
116
117 let fastreturn = match *i.kind {
118 // nothing left to do for traits (don't want to filter their
119 // methods out, visibility controlled by the trait)
120 clean::TraitItem(..) => true,
121
122 // implementations of traits are always public.
123 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
124 // Variant fields have inherited visibility
125 clean::VariantItem(clean::Variant {
126 kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
127 ..
128 }) => 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, 'tcx> {
150 pub(crate) tcx: TyCtxt<'tcx>,
151 pub(crate) retained: &'a ItemIdSet,
152 pub(crate) cache: &'a Cache,
153 pub(crate) is_json_output: bool,
154 pub(crate) document_private: bool,
155 }
156
157 impl<'a> ImplStripper<'a, '_> {
158 #[inline]
159 fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
160 if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
161 true
162 } else if self.is_json_output {
163 // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
164 // need to keep it.
165 self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
166 && !item.attrs.lists(sym::doc).has_word(sym::hidden)
167 } else {
168 false
169 }
170 }
171 }
172
173 impl<'a> DocFolder for ImplStripper<'a, '_> {
174 fn fold_item(&mut self, i: Item) -> Option<Item> {
175 if let clean::ImplItem(ref imp) = *i.kind {
176 // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
177 // documentation.
178 //
179 // There is one special case: if the impl block contains only private items.
180 if imp.trait_.is_none() {
181 // If the only items present are private ones and we're not rendering private items,
182 // we don't document it.
183 if !imp.items.is_empty()
184 && !self.document_private
185 && imp.items.iter().all(|i| {
186 let item_id = i.item_id;
187 item_id.is_local()
188 && !is_item_reachable(
189 self.tcx,
190 self.is_json_output,
191 &self.cache.effective_visibilities,
192 item_id,
193 )
194 })
195 {
196 return None;
197 } else if imp.items.is_empty() && i.doc_value().is_none() {
198 return None;
199 }
200 }
201 // Because we don't inline in `maybe_inline_local` if the output format is JSON,
202 // we need to make a special check for JSON output: we want to keep it unless it has
203 // a `#[doc(hidden)]` attribute if the `for_` type is exported.
204 if let Some(did) = imp.for_.def_id(self.cache) &&
205 !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did)
206 {
207 debug!("ImplStripper: impl item for stripped type; removing");
208 return None;
209 }
210 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) &&
211 !self.should_keep_impl(&i, did) {
212 debug!("ImplStripper: impl item for stripped trait; removing");
213 return None;
214 }
215 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
216 for typaram in generics {
217 if let Some(did) = typaram.def_id(self.cache) && !self.should_keep_impl(&i, did)
218 {
219 debug!(
220 "ImplStripper: stripped item in trait's generics; removing impl"
221 );
222 return None;
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<'tcx> {
233 pub(crate) tcx: TyCtxt<'tcx>,
234 pub(crate) is_json_output: bool,
235 }
236
237 impl<'tcx> ImportStripper<'tcx> {
238 fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
239 if self.is_json_output {
240 // FIXME: This should be handled the same way as for HTML output.
241 imp.imported_item_is_doc_hidden(self.tcx)
242 } else {
243 i.attrs.lists(sym::doc).has_word(sym::hidden)
244 }
245 }
246 }
247
248 impl<'tcx> DocFolder for ImportStripper<'tcx> {
249 fn fold_item(&mut self, i: Item) -> Option<Item> {
250 match *i.kind {
251 clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None,
252 clean::ImportItem(_) if i.attrs.lists(sym::doc).has_word(sym::hidden) => None,
253 clean::ExternCrateItem { .. } | clean::ImportItem(..)
254 if i.visibility(self.tcx) != Some(Visibility::Public) =>
255 {
256 None
257 }
258 _ => Some(self.fold_item_recur(i)),
259 }
260 }
261 }