rustdoc/passes/
stripper.rs
1use 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::RequiredMethodItem(..)
125 | clean::RequiredAssocConstItem(..)
126 | clean::RequiredAssocTypeItem(..) => {}
127
128 clean::ProcMacroItem(..) => {}
130
131 clean::PrimitiveItem(..) => {}
133
134 clean::KeywordItem => {}
136 }
137
138 let fastreturn = match i.kind {
139 clean::TraitItem(..) => true,
142
143 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
145 clean::VariantItem(clean::Variant {
147 kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
148 ..
149 }) => true,
150 _ => false,
151 };
152
153 let i = if fastreturn {
154 if self.update_retained {
155 self.retained.insert(i.item_id);
156 }
157 return Some(i);
158 } else {
159 self.fold_item_recur(i)
160 };
161
162 if self.update_retained {
163 self.retained.insert(i.item_id);
164 }
165 Some(i)
166 }
167}
168
169pub(crate) struct ImplStripper<'a, 'tcx> {
171 pub(crate) tcx: TyCtxt<'tcx>,
172 pub(crate) retained: &'a ItemIdSet,
173 pub(crate) cache: &'a Cache,
174 pub(crate) is_json_output: bool,
175 pub(crate) document_private: bool,
176 pub(crate) document_hidden: bool,
177}
178
179impl ImplStripper<'_, '_> {
180 #[inline]
181 fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
182 if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
183 true
184 } else if self.is_json_output {
185 self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
188 && (self.document_hidden
189 || ((!item.is_doc_hidden()
190 && for_def_id
191 .as_local()
192 .map(|def_id| !inherits_doc_hidden(self.tcx, def_id, None))
193 .unwrap_or(true))
194 || self.cache.inlined_items.contains(&for_def_id)))
195 } else {
196 false
197 }
198 }
199}
200
201impl DocFolder for ImplStripper<'_, '_> {
202 fn fold_item(&mut self, i: Item) -> Option<Item> {
203 if let clean::ImplItem(ref imp) = i.kind {
204 if imp.trait_.is_none() {
209 if !imp.items.is_empty()
212 && !self.document_private
213 && imp.items.iter().all(|i| {
214 let item_id = i.item_id;
215 item_id.is_local()
216 && !self
217 .cache
218 .effective_visibilities
219 .is_reachable(self.tcx, item_id.expect_def_id())
220 })
221 {
222 debug!("ImplStripper: no public item; removing {imp:?}");
223 return None;
224 } else if imp.items.is_empty() && i.doc_value().is_empty() {
225 debug!("ImplStripper: no item and no doc; removing {imp:?}");
226 return None;
227 }
228 }
229 if let Some(did) = imp.for_.def_id(self.cache)
233 && !imp.for_.is_assoc_ty()
234 && !self.should_keep_impl(&i, did)
235 {
236 debug!("ImplStripper: impl item for stripped type; removing {imp:?}");
237 return None;
238 }
239 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id())
240 && !self.should_keep_impl(&i, did)
241 {
242 debug!("ImplStripper: impl item for stripped trait; removing {imp:?}");
243 return None;
244 }
245 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
246 for typaram in generics {
247 if let Some(did) = typaram.def_id(self.cache)
248 && !self.should_keep_impl(&i, did)
249 {
250 debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}");
251 return None;
252 }
253 }
254 }
255 }
256 Some(self.fold_item_recur(i))
257 }
258}
259
260pub(crate) struct ImportStripper<'tcx> {
262 pub(crate) tcx: TyCtxt<'tcx>,
263 pub(crate) is_json_output: bool,
264 pub(crate) document_hidden: bool,
265}
266
267impl ImportStripper<'_> {
268 fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
269 if self.is_json_output {
270 imp.imported_item_is_doc_hidden(self.tcx)
272 } else {
273 i.is_doc_hidden()
274 }
275 }
276}
277
278impl DocFolder for ImportStripper<'_> {
279 fn fold_item(&mut self, i: Item) -> Option<Item> {
280 match &i.kind {
281 clean::ImportItem(imp)
282 if !self.document_hidden && self.import_should_be_hidden(&i, imp) =>
283 {
284 None
285 }
286 clean::ExternCrateItem { .. } | clean::ImportItem(..)
288 if i.visibility(self.tcx) != Some(Visibility::Public) =>
289 {
290 None
291 }
292 _ => Some(self.fold_item_recur(i)),
293 }
294 }
295}