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, ItemKind};
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 ItemKind::Stripped(..) => {
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 ItemKind::TypeAlias(..)
55 | ItemKind::Static(..)
56 | ItemKind::Struct(..)
57 | ItemKind::Enum(..)
58 | ItemKind::Trait(..)
59 | ItemKind::Function(..)
60 | ItemKind::Variant(..)
61 | ItemKind::ForeignFunction(..)
62 | ItemKind::ForeignStatic(..)
63 | ItemKind::Constant(..)
64 | ItemKind::Union(..)
65 | ItemKind::TraitAlias(..)
66 | ItemKind::DeclMacro(..)
67 | ItemKind::ForeignType => {
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 ItemKind::AssocFunction(clean::AssocFunction { body: Some(_), .. })
83 | ItemKind::AssocConstant(clean::AssocConstant { body: Some(_), .. })
84 | ItemKind::AssocType(clean::AssocType { type_: Some(_), .. }) => {
85 let item_id = i.item_id;
86 if item_id.is_local()
87 && !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id())
88 {
89 debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
90 return None;
91 }
92 }
93
94 ItemKind::StructField(..) => {
95 if i.visibility(self.tcx) != Some(Visibility::Public) {
96 return Some(strip_item(i));
97 }
98 }
99
100 ItemKind::Module(..) => {
101 if i.item_id.is_local()
102 && !is_item_reachable(
103 self.tcx,
104 self.is_json_output,
105 self.effective_visibilities,
106 i.item_id,
107 )
108 {
109 debug!("Stripper: stripping module {:?}", i.name);
110 let old = mem::replace(&mut self.update_retained, false);
111 let ret = strip_item(self.fold_item_recur(i));
112 self.update_retained = old;
113 return Some(ret);
114 }
115 }
116
117 ItemKind::ExternCrate { .. } | ItemKind::Import(_) => {}
119
120 ItemKind::Impl(..) => {}
121
122 ItemKind::PlaceholderImpl => return None,
125
126 ItemKind::AssocFunction(clean::AssocFunction { body: None, .. })
128 | ItemKind::AssocConstant(clean::AssocConstant { body: None, .. })
129 | ItemKind::AssocType(clean::AssocType { type_: None, .. }) => {}
130
131 ItemKind::ProcMacro(..) => {}
133
134 ItemKind::PrimitiveType(..) => {}
136
137 ItemKind::Keyword => {}
139 ItemKind::Attribute => {}
141 }
142
143 let fastreturn = match i.kind {
144 ItemKind::Trait(..) => true,
147
148 ItemKind::Impl(ref imp) if imp.trait_.is_some() => true,
150 ItemKind::Variant(clean::Variant {
152 kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..),
153 ..
154 }) => true,
155 _ => false,
156 };
157
158 let i = if fastreturn {
159 if self.update_retained {
160 self.retained.insert(i.item_id);
161 }
162 return Some(i);
163 } else {
164 self.fold_item_recur(i)
165 };
166
167 if self.update_retained {
168 self.retained.insert(i.item_id);
169 }
170 Some(i)
171 }
172}
173
174pub(crate) struct ImplStripper<'a, 'tcx> {
176 pub(crate) tcx: TyCtxt<'tcx>,
177 pub(crate) retained: &'a ItemIdSet,
178 pub(crate) cache: &'a Cache,
179 pub(crate) is_json_output: bool,
180 pub(crate) document_private: bool,
181 pub(crate) document_hidden: bool,
182}
183
184impl ImplStripper<'_, '_> {
185 #[inline]
186 fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
187 if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
188 true
189 } else if self.is_json_output {
190 self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
193 && (self.document_hidden
194 || ((!item.is_doc_hidden()
195 && for_def_id
196 .as_local()
197 .map(|def_id| !inherits_doc_hidden(self.tcx, def_id, None))
198 .unwrap_or(true))
199 || self.cache.inlined_items.contains(&for_def_id)))
200 } else {
201 false
202 }
203 }
204}
205
206impl DocFolder for ImplStripper<'_, '_> {
207 fn fold_item(&mut self, i: Item) -> Option<Item> {
208 if let ItemKind::Impl(ref imp) = i.kind {
209 if imp.trait_.is_none() {
214 if !imp.items.is_empty()
217 && !self.document_private
218 && imp.items.iter().all(|i| {
219 let item_id = i.item_id;
220 item_id.is_local()
221 && !self
222 .cache
223 .effective_visibilities
224 .is_reachable(self.tcx, item_id.expect_def_id())
225 })
226 {
227 debug!("ImplStripper: no public item; removing {imp:?}");
228 return None;
229 } else if imp.items.is_empty() && i.doc_value().is_empty() {
230 debug!("ImplStripper: no item and no doc; removing {imp:?}");
231 return None;
232 }
233 }
234 if let Some(did) = imp.for_.def_id(self.cache)
238 && !imp.for_.is_assoc_ty()
239 && !self.should_keep_impl(&i, did)
240 {
241 debug!("ImplStripper: impl item for stripped type; removing {imp:?}");
242 return None;
243 }
244 if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id())
245 && !self.should_keep_impl(&i, did)
246 {
247 debug!("ImplStripper: impl item for stripped trait; removing {imp:?}");
248 return None;
249 }
250 if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
251 for typaram in generics {
252 if let Some(did) = typaram.def_id(self.cache)
253 && !self.should_keep_impl(&i, did)
254 {
255 debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}");
256 return None;
257 }
258 }
259 }
260 }
261 Some(self.fold_item_recur(i))
262 }
263}
264
265pub(crate) struct ImportStripper<'tcx> {
267 pub(crate) tcx: TyCtxt<'tcx>,
268 pub(crate) is_json_output: bool,
269 pub(crate) document_hidden: bool,
270 pub(crate) document_private: 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 ItemKind::Import(imp)
288 if !self.document_hidden && self.import_should_be_hidden(&i, imp) =>
289 {
290 debug!("ImportStripper: stripping {:?}", i.name);
291 None
292 }
293 ItemKind::Import(imp) if self.document_private => Some(self.fold_item_recur(i)),
294 ItemKind::ExternCrate { .. } | ItemKind::Import(..)
295 if i.visibility(self.tcx) != Some(Visibility::Public) =>
296 {
297 debug!("ImportStripper: stripping {:?}", i.name);
298 None
299 }
300 _ => Some(self.fold_item_recur(i)),
301 }
302 }
303}