Skip to content

Commit 6c991b0

Browse files
committed
Auto merge of #107000 - GuillaumeGomez:fix-items-in-doc-hidden-block, r=notriddle,petrochenkov
Fix handling of items inside a `doc(hidden)` block Fixes #106373. cc `@aDotInTheVoid` r? `@notriddle`
2 parents f312650 + ea84418 commit 6c991b0

11 files changed

+357
-111
lines changed

src/librustdoc/clean/mod.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -2213,21 +2213,17 @@ fn clean_maybe_renamed_item<'tcx>(
22132213
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
22142214
}
22152215

2216-
if !extra_attrs.is_empty() {
2216+
let mut item = if !extra_attrs.is_empty() {
22172217
extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
22182218
let attrs = Attributes::from_ast(&extra_attrs);
22192219
let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
22202220

2221-
vec![Item::from_def_id_and_attrs_and_parts(
2222-
def_id,
2223-
Some(name),
2224-
kind,
2225-
Box::new(attrs),
2226-
cfg,
2227-
)]
2221+
Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg)
22282222
} else {
2229-
vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)]
2230-
}
2223+
Item::from_def_id_and_parts(def_id, Some(name), kind, cx)
2224+
};
2225+
item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id());
2226+
vec![item]
22312227
})
22322228
}
22332229

src/librustdoc/html/render/write_shared.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub(super) fn write_shared(
138138
Ok((ret, krates))
139139
}
140140

141-
/// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
141+
/// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
142142
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
143143
///
144144
/// This forms the payload of files that look like this:

src/librustdoc/passes/strip_hidden.rs

+83-26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
//! Strip all doc(hidden) items from the output.
2+
3+
use rustc_middle::ty::TyCtxt;
24
use rustc_span::symbol::sym;
35
use std::mem;
46

@@ -7,6 +9,7 @@ use crate::clean::{Item, ItemIdSet, NestedAttributesExt};
79
use crate::core::DocContext;
810
use crate::fold::{strip_item, DocFolder};
911
use crate::passes::{ImplStripper, Pass};
12+
use crate::visit_ast::inherits_doc_hidden;
1013

1114
pub(crate) const STRIP_HIDDEN: Pass = Pass {
1215
name: "strip-hidden",
@@ -21,7 +24,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
2124

2225
// strip all #[doc(hidden)] items
2326
let krate = {
24-
let mut stripper = Stripper { retained: &mut retained, update_retained: true };
27+
let mut stripper = Stripper {
28+
retained: &mut retained,
29+
update_retained: true,
30+
tcx: cx.tcx,
31+
is_in_hidden_item: false,
32+
};
2533
stripper.fold_crate(krate)
2634
};
2735

@@ -36,40 +44,89 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
3644
stripper.fold_crate(krate)
3745
}
3846

39-
struct Stripper<'a> {
47+
struct Stripper<'a, 'tcx> {
4048
retained: &'a mut ItemIdSet,
4149
update_retained: bool,
50+
tcx: TyCtxt<'tcx>,
51+
is_in_hidden_item: bool,
52+
}
53+
54+
impl<'a, 'tcx> Stripper<'a, 'tcx> {
55+
fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item {
56+
let prev = self.is_in_hidden_item;
57+
self.is_in_hidden_item |= is_in_hidden_item;
58+
let ret = self.fold_item_recur(i);
59+
self.is_in_hidden_item = prev;
60+
ret
61+
}
62+
63+
/// In case `i` is a non-hidden impl block, then we special-case it by changing the value
64+
/// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
65+
fn recurse_in_impl(&mut self, i: Item) -> Item {
66+
let prev = mem::replace(&mut self.is_in_hidden_item, false);
67+
let ret = self.fold_item_recur(i);
68+
self.is_in_hidden_item = prev;
69+
ret
70+
}
4271
}
4372

44-
impl<'a> DocFolder for Stripper<'a> {
73+
impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
4574
fn fold_item(&mut self, i: Item) -> Option<Item> {
46-
if i.attrs.lists(sym::doc).has_word(sym::hidden) {
47-
debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
48-
// Use a dedicated hidden item for fields, variants, and modules.
49-
// We need to keep private fields and variants, so that the docs
50-
// can show a placeholder "// some variants omitted". We need to keep
51-
// private modules, because they can contain impl blocks, and impl
52-
// block privacy is inherited from the type and trait, not from the
53-
// module it's defined in. Both of these are marked "stripped," and
54-
// not included in the final docs, but since they still have an effect
55-
// on the final doc, cannot be completely removed from the Clean IR.
56-
match *i.kind {
57-
clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
58-
// We need to recurse into stripped modules to
59-
// strip things like impl methods but when doing so
60-
// we must not add any items to the `retained` set.
61-
let old = mem::replace(&mut self.update_retained, false);
62-
let ret = strip_item(self.fold_item_recur(i));
63-
self.update_retained = old;
64-
return Some(ret);
65-
}
66-
_ => return None,
75+
let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
76+
let is_impl = matches!(*i.kind, clean::ImplItem(..));
77+
let mut is_hidden = has_doc_hidden;
78+
if !is_impl {
79+
is_hidden = self.is_in_hidden_item || has_doc_hidden;
80+
if !is_hidden && i.inline_stmt_id.is_none() {
81+
// We don't need to check if it's coming from a reexport since the reexport itself was
82+
// already checked.
83+
is_hidden = i
84+
.item_id
85+
.as_def_id()
86+
.and_then(|def_id| def_id.as_local())
87+
.map(|def_id| inherits_doc_hidden(self.tcx, def_id))
88+
.unwrap_or(false);
6789
}
68-
} else {
90+
}
91+
if !is_hidden {
6992
if self.update_retained {
7093
self.retained.insert(i.item_id);
7194
}
95+
return Some(if is_impl {
96+
self.recurse_in_impl(i)
97+
} else {
98+
self.set_is_in_hidden_item_and_fold(false, i)
99+
});
100+
}
101+
debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
102+
// Use a dedicated hidden item for fields, variants, and modules.
103+
// We need to keep private fields and variants, so that the docs
104+
// can show a placeholder "// some variants omitted". We need to keep
105+
// private modules, because they can contain impl blocks, and impl
106+
// block privacy is inherited from the type and trait, not from the
107+
// module it's defined in. Both of these are marked "stripped," and
108+
// not included in the final docs, but since they still have an effect
109+
// on the final doc, cannot be completely removed from the Clean IR.
110+
match *i.kind {
111+
clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
112+
// We need to recurse into stripped modules to
113+
// strip things like impl methods but when doing so
114+
// we must not add any items to the `retained` set.
115+
let old = mem::replace(&mut self.update_retained, false);
116+
let ret = strip_item(self.set_is_in_hidden_item_and_fold(true, i));
117+
self.update_retained = old;
118+
Some(ret)
119+
}
120+
_ => {
121+
let ret = self.set_is_in_hidden_item_and_fold(true, i);
122+
if has_doc_hidden {
123+
// If the item itself has `#[doc(hidden)]`, then we simply remove it.
124+
None
125+
} else {
126+
// However if it's a "descendant" of a `#[doc(hidden)]` item, then we strip it.
127+
Some(strip_item(ret))
128+
}
129+
}
72130
}
73-
Some(self.fold_item_recur(i))
74131
}
75132
}

0 commit comments

Comments
 (0)