rustdoc/passes/
stripper.rs1use std::mem;
4
5use rustc_hir::def_id::DefId;
6use rustc_middle::ty::{TyCtxt, Visibility};
7use tracing::debug;
8
9use crate::clean::utils::inherits_doc_hidden;
10use crate::clean::{self, Item, ItemId, ItemIdSet};
11use crate::fold::{DocFolder, strip_item};
12use crate::formats::cache::Cache;
13use crate::visit_lib::RustdocEffectiveVisibilities;
14
15pub(crate) struct Stripper<'a, 'tcx> {
16 pub(crate) retained: &'a mut ItemIdSet,
17 pub(crate) effective_visibilities: &'a RustdocEffectiveVisibilities,
18 pub(crate) update_retained: bool,
19 pub(crate) is_json_output: bool,
20 pub(crate) tcx: TyCtxt<'tcx>,
21}
22
23#[inline]
27fn is_item_reachable(
28 tcx: TyCtxt<'_>,
29 is_json_output: bool,
30 effective_visibilities: &RustdocEffectiveVisibilities,
31 item_id: ItemId,
32) -> bool {
33 if is_json_output {
34 effective_visibilities.is_reachable(tcx, item_id.expect_def_id())
35 } else {
36 effective_visibilities.is_exported(tcx, item_id.expect_def_id())
37 }
38}
39
40impl DocFolder for Stripper<'_, '_> {
41 fn fold_item(&mut self, i: Item) -> Option<Item> {
42 match i.kind {
43 clean::StrippedItem(..) => {
44 debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
48 let old = mem::replace(&mut self.update_retained, false);
49 let ret = self.fold_item_recur(i);
50 self.update_retained = old;
51 return Some(ret);
52 }
53 clean::TypeAliasItem(..)
55 | clean::StaticItem(..)
56 | clean::StructItem(..)
57 | clean::EnumItem(..)
58 | clean::TraitItem(..)
59 | clean::FunctionItem(..)
60 | clean::VariantItem(..)
61 | clean::ForeignFunctionItem(..)
62 | clean::ForeignStaticItem(..)
63 | clean::ConstantItem(..)
64 | clean::UnionItem(..)
65 | clean::TraitAliasItem(..)
66 | clean::MacroItem(..)
67 | clean::ForeignTypeItem => {
68 let item_id = i.item_id;
69 if item_id.is_local()
70 && !is_item_reachable(
71 self.tcx,
72 self.is_json_output,
73 self.effective_visibilities,
74 item_id,
75 )
76 {
77 debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
78 return None;
79 }
80 }
81
82 clean::MethodItem(..)
83 | clean::ProvidedAssocConstItem(..)
84 | clean::ImplAssocConstItem(..)
85 | clean::AssocTypeItem(..) => {
86 let item_id = i.item_id;
87 if item_id.is_local()
88 && !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id())
89 {
90 debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
91 return None;
92 }
93 }
94
95 clean::StructFieldItem(..) => {
96 if i.visibility(self.tcx) != Some(Visibility::Public) {
97 return Some(strip_item(i));
98 }
99 }
100
101 clean::ModuleItem(..) => {
102 if i.item_id.is_local()
103 && !is_item_reachable(
104 self.tcx,
105 self.is_json_output,
106 self.effective_visibilities,
107 i.item_id,
108 )
109 {
110 debug!("Stripper: stripping module {:?}", i.name);
111 let old = mem::replace(&mut self.update_retained, false);
112 let ret = strip_item(self.fold_item_recur(i));
113 self.update_retained = old;
114 return Some(ret);
115 }
116 }
117
118 clean::ExternCrateItem { .. } | clean::ImportItem(_) => {}
120
121 clean::ImplItem(..) => {}
122
123 clean::PlaceholderImplItem => return None,
126
127 clean::RequiredMethodItem(..)
129 | clean::RequiredAssocConstItem(..)
130 | clean::RequiredAssocTypeItem(..) => {}
131
132 clean::ProcMacroItem(..) => {}
134
135 clean::PrimitiveItem(..) => {}
137
138 clean::KeywordItem => {}
140 clean::AttributeItem => {}
142 }
143
144 let fastreturn = match i.kind {
145 clean::TraitItem(..) => true,
148
149 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
151 clean::VariantItem(clean::Variant {
153 kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
154 ..
155 }) => true,
156 _ => false,
157 };
158
159 let i = if fastreturn {
160 if self.update_retained {
161 self.retained.insert(i.item_id);
162 }
163 return Some(i);
164 } else {
165 self.fold_item_recur(i)
166 };
167
168 if self.update_retained {
169 self.retained.insert(i.item_id);
170 }
171 Some(i)
172 }
173}
174
175pub(crate) struct ImplStripper<'a, 'tcx> {
177 pub(crate) tcx: TyCtxt<'tcx>,
178 pub(crate) retained: &'a ItemIdSet,
179 pub(crate) cache: &'a Cache,
180 pub(crate) is_json_output: bool,
181 pub(crate) document_private: bool,
182 pub(crate) document_hidden: bool,
183}
184
185impl ImplStripper<'_, '_> {
186 #[inline]
187 fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
188 if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
189 true
190 } else if self.is_json_output {
191 self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
194 && (self.document_hidden
195 || ((!item.is_doc_hidden()
196 && for_def_id
197 .as_local()
198 .map(|def_id| !inherits_doc_hidden(self.tcx, def_id, None))
199 .unwrap_or(true))
200 || self.cache.inlined_items.contains(&for_def_id)))
201 } else {
202 false
203 }
204 }
205}
206
207impl DocFolder for ImplStripper<'_, '_> {
208 fn fold_item(&mut self, i: Item) -> Option<Item> {
209 if let clean::ImplItem(ref imp) = i.kind {
210 if imp.trait_.is_none() {
215 if !imp.items.is_empty()
218 && !self.document_private
219 && imp.items.iter().all(|i| {
220 let item_id = i.item_id;
221 item_id.is_local()
222 && !self
223 .cache
224 .effective_visibilities
225 .is_reachable(self.tcx, item_id.expect_def_id())
226 })
227 {
228 debug!("ImplStripper: no public item; removing {imp:?}");
229 return None;
230 } else if imp.items.is_empty() && i.doc_value().is_empty() {
231 debug!("ImplStripper: no item and no doc; removing {imp:?}");
232 return None;
233 }
234 }
235 if let Some(did) = imp.for_.def_id(self.cache)
239 && !imp.for_.is_assoc_ty()
240 && !self.should_keep_impl(&i, did)
241 {
242 debug!("ImplStripper: impl item for stripped type; removing {imp:?}");
243 return None;
244 }
245 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id())
246 && !self.should_keep_impl(&i, did)
247 {
248 debug!("ImplStripper: impl item for stripped trait; removing {imp:?}");
249 return None;
250 }
251 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
252 for typaram in generics {
253 if let Some(did) = typaram.def_id(self.cache)
254 && !self.should_keep_impl(&i, did)
255 {
256 debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}");
257 return None;
258 }
259 }
260 }
261 }
262 Some(self.fold_item_recur(i))
263 }
264}
265
266pub(crate) struct ImportStripper<'tcx> {
268 pub(crate) tcx: TyCtxt<'tcx>,
269 pub(crate) is_json_output: bool,
270 pub(crate) document_hidden: bool,
271}
272
273impl ImportStripper<'_> {
274 fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
275 if self.is_json_output {
276 imp.imported_item_is_doc_hidden(self.tcx)
278 } else {
279 i.is_doc_hidden()
280 }
281 }
282}
283
284impl DocFolder for ImportStripper<'_> {
285 fn fold_item(&mut self, i: Item) -> Option<Item> {
286 match &i.kind {
287 clean::ImportItem(imp)
288 if !self.document_hidden && self.import_should_be_hidden(&i, imp) =>
289 {
290 None
291 }
292 clean::ExternCrateItem { .. } | clean::ImportItem(..)
294 if i.visibility(self.tcx) != Some(Visibility::Public) =>
295 {
296 None
297 }
298 _ => Some(self.fold_item_recur(i)),
299 }
300 }
301}