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