rustdoc/passes/
strip_hidden.rs1use std::mem;
4
5use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
6use rustc_middle::ty::TyCtxt;
7use tracing::debug;
8
9use crate::clean::utils::inherits_doc_hidden;
10use crate::clean::{self, Item, ItemIdSet, reexport_chain};
11use crate::core::DocContext;
12use crate::fold::{DocFolder, strip_item};
13use crate::passes::{ImplStripper, Pass};
14
15pub(crate) const STRIP_HIDDEN: Pass = Pass {
16 name: "strip-hidden",
17 run: Some(strip_hidden),
18 description: "strips all `#[doc(hidden)]` items from the output",
19};
20
21pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
23 let mut retained = ItemIdSet::default();
24 let is_json_output = cx.is_json_output();
25
26 let krate = {
28 let mut stripper = Stripper {
29 retained: &mut retained,
30 update_retained: true,
31 tcx: cx.tcx,
32 is_in_hidden_item: false,
33 last_reexport: None,
34 };
35 stripper.fold_crate(krate)
36 };
37
38 let mut stripper = ImplStripper {
40 tcx: cx.tcx,
41 retained: &retained,
42 cache: &cx.cache,
43 is_json_output,
44 document_private: cx.document_private(),
45 document_hidden: cx.document_hidden(),
46 };
47 stripper.fold_crate(krate)
48}
49
50struct Stripper<'a, 'tcx> {
51 retained: &'a mut ItemIdSet,
52 update_retained: bool,
53 tcx: TyCtxt<'tcx>,
54 is_in_hidden_item: bool,
55 last_reexport: Option<LocalDefId>,
56}
57
58impl Stripper<'_, '_> {
59 fn set_last_reexport_then_fold_item(&mut self, i: Item) -> Item {
60 let prev_from_reexport = self.last_reexport;
61 if i.inline_stmt_id.is_some() {
62 self.last_reexport = i.item_id.as_def_id().and_then(|def_id| def_id.as_local());
63 }
64 let ret = self.fold_item_recur(i);
65 self.last_reexport = prev_from_reexport;
66 ret
67 }
68
69 fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item {
70 let prev = self.is_in_hidden_item;
71 self.is_in_hidden_item |= is_in_hidden_item;
72 let ret = self.set_last_reexport_then_fold_item(i);
73 self.is_in_hidden_item = prev;
74 ret
75 }
76
77 fn recurse_in_impl_or_exported_macro(&mut self, i: Item) -> Item {
80 let prev = mem::replace(&mut self.is_in_hidden_item, false);
81 let ret = self.set_last_reexport_then_fold_item(i);
82 self.is_in_hidden_item = prev;
83 ret
84 }
85}
86
87impl DocFolder for Stripper<'_, '_> {
88 fn fold_item(&mut self, i: Item) -> Option<Item> {
89 let has_doc_hidden = i.is_doc_hidden();
90
91 if let clean::ImportItem(clean::Import { source, .. }) = &i.kind
92 && let Some(source_did) = source.did
93 {
94 if self.tcx.is_doc_hidden(source_did) {
95 return None;
96 } else if let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local()) {
97 let reexports = reexport_chain(self.tcx, import_def_id, source_did);
98
99 let has_hidden_source = reexports
101 .iter()
102 .filter_map(|reexport| reexport.id())
103 .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did));
104
105 if has_hidden_source {
106 return None;
107 }
108 }
109 }
110
111 let is_impl_or_exported_macro = match i.kind {
112 clean::ImplItem(..) => true,
113 clean::MacroItem(..) => i.is_exported_macro(),
116 _ => false,
117 };
118 let mut is_hidden = has_doc_hidden;
119 if !is_impl_or_exported_macro {
120 is_hidden = self.is_in_hidden_item || has_doc_hidden;
121 if !is_hidden && i.inline_stmt_id.is_none() {
122 is_hidden = i
132 .item_id
133 .as_def_id()
134 .and_then(|def_id| def_id.as_local())
135 .map(|def_id| inherits_doc_hidden(self.tcx, def_id, self.last_reexport))
136 .unwrap_or(false);
137 }
138 }
139 if !is_hidden {
140 if self.update_retained {
141 self.retained.insert(i.item_id);
142 }
143 return Some(if is_impl_or_exported_macro {
144 self.recurse_in_impl_or_exported_macro(i)
145 } else {
146 self.set_is_in_hidden_item_and_fold(false, i)
147 });
148 }
149 debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
150 match i.kind {
159 clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
160 let old = mem::replace(&mut self.update_retained, false);
164 let ret = self.set_is_in_hidden_item_and_fold(true, i);
165 self.update_retained = old;
166 if ret.item_id == clean::ItemId::DefId(CRATE_DEF_ID.into()) {
167 debug!("strip_hidden: Not stripping local crate");
169 Some(ret)
170 } else {
171 Some(strip_item(ret))
172 }
173 }
174 _ => {
175 let ret = self.set_is_in_hidden_item_and_fold(true, i);
176 if has_doc_hidden {
177 None
179 } else {
180 Some(strip_item(ret))
182 }
183 }
184 }
185 }
186}