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