Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of items inside a doc(hidden) block #107000

Merged
merged 10 commits into from
Feb 3, 2023
16 changes: 6 additions & 10 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2225,21 +2225,17 @@ fn clean_maybe_renamed_item<'tcx>(
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
}

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

vec![Item::from_def_id_and_attrs_and_parts(
def_id,
Some(name),
kind,
Box::new(attrs),
cfg,
)]
Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg)
} else {
vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)]
}
Item::from_def_id_and_parts(def_id, Some(name), kind, cx)
};
item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id());
vec![item]
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/html/render/write_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub(super) fn write_shared(
Ok((ret, krates))
}

/// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
/// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
///
/// This forms the payload of files that look like this:
Expand Down
109 changes: 83 additions & 26 deletions src/librustdoc/passes/strip_hidden.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Strip all doc(hidden) items from the output.
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
use std::mem;

Expand All @@ -7,6 +9,7 @@ use crate::clean::{Item, ItemIdSet, NestedAttributesExt};
use crate::core::DocContext;
use crate::fold::{strip_item, DocFolder};
use crate::passes::{ImplStripper, Pass};
use crate::visit_ast::inherits_doc_hidden;

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

// strip all #[doc(hidden)] items
let krate = {
let mut stripper = Stripper { retained: &mut retained, update_retained: true };
let mut stripper = Stripper {
retained: &mut retained,
update_retained: true,
tcx: cx.tcx,
is_in_hidden_item: false,
};
stripper.fold_crate(krate)
};

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

struct Stripper<'a> {
struct Stripper<'a, 'tcx> {
retained: &'a mut ItemIdSet,
update_retained: bool,
tcx: TyCtxt<'tcx>,
is_in_hidden_item: bool,
}

impl<'a, 'tcx> Stripper<'a, 'tcx> {
fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item {
let prev = self.is_in_hidden_item;
self.is_in_hidden_item |= is_in_hidden_item;
let ret = self.fold_item_recur(i);
self.is_in_hidden_item = prev;
ret
}

/// In case `i` is a non-hidden impl block, then we special-case it by changing the value
/// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
fn recurse_in_impl(&mut self, i: Item) -> Item {
let prev = mem::replace(&mut self.is_in_hidden_item, false);
let ret = self.fold_item_recur(i);
self.is_in_hidden_item = prev;
ret
}
}

impl<'a> DocFolder for Stripper<'a> {
impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if i.attrs.lists(sym::doc).has_word(sym::hidden) {
debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
// Use a dedicated hidden item for fields, variants, and modules.
// We need to keep private fields and variants, so that the docs
// can show a placeholder "// some variants omitted". We need to keep
// private modules, because they can contain impl blocks, and impl
// block privacy is inherited from the type and trait, not from the
// module it's defined in. Both of these are marked "stripped," and
// not included in the final docs, but since they still have an effect
// on the final doc, cannot be completely removed from the Clean IR.
match *i.kind {
clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
// We need to recurse into stripped modules to
// strip things like impl methods but when doing so
// we must not add any items to the `retained` set.
let old = mem::replace(&mut self.update_retained, false);
let ret = strip_item(self.fold_item_recur(i));
self.update_retained = old;
return Some(ret);
}
_ => return None,
let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
let is_impl = matches!(*i.kind, clean::ImplItem(..));
let mut is_hidden = has_doc_hidden;
if !is_impl {
is_hidden = self.is_in_hidden_item || has_doc_hidden;
if !is_hidden && i.inline_stmt_id.is_none() {
// We don't need to check if it's coming from a reexport since the reexport itself was
// already checked.
is_hidden = i
.item_id
.as_def_id()
.and_then(|def_id| def_id.as_local())
.map(|def_id| inherits_doc_hidden(self.tcx, def_id))
.unwrap_or(false);
}
} else {
}
if !is_hidden {
if self.update_retained {
self.retained.insert(i.item_id);
}
return Some(if is_impl {
self.recurse_in_impl(i)
} else {
self.set_is_in_hidden_item_and_fold(false, i)
});
}
debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
// Use a dedicated hidden item for fields, variants, and modules.
// We need to keep private fields and variants, so that the docs
// can show a placeholder "// some variants omitted". We need to keep
// private modules, because they can contain impl blocks, and impl
// block privacy is inherited from the type and trait, not from the
// module it's defined in. Both of these are marked "stripped," and
// not included in the final docs, but since they still have an effect
// on the final doc, cannot be completely removed from the Clean IR.
match *i.kind {
clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
// We need to recurse into stripped modules to
// strip things like impl methods but when doing so
// we must not add any items to the `retained` set.
let old = mem::replace(&mut self.update_retained, false);
let ret = strip_item(self.set_is_in_hidden_item_and_fold(true, i));
self.update_retained = old;
Some(ret)
}
_ => {
let ret = self.set_is_in_hidden_item_and_fold(true, i);
if has_doc_hidden {
// If the item itself has `#[doc(hidden)]`, then we simply remove it.
None
} else {
// However if it's a "descendant" of a `#[doc(hidden)]` item, then we strip it.
Some(strip_item(ret))
}
}
}
Some(self.fold_item_recur(i))
}
}
Loading