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