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

[rustdoc] Don't render impl blocks with doc comment if they only contain private items by default #100323

Merged
merged 2 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/librustdoc/passes/strip_hidden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
/// Strip items marked `#[doc(hidden)]`
pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
let mut retained = ItemIdSet::default();
let is_json_output = cx.output_format.is_json() && !cx.show_coverage;

// strip all #[doc(hidden)] items
let krate = {
Expand All @@ -25,7 +26,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
};

// strip all impls referencing stripped items
let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
let mut stripper = ImplStripper {
retained: &retained,
cache: &cx.cache,
is_json_output,
document_private: cx.render_options.document_private,
};
stripper.fold_crate(krate)
}

Expand Down
10 changes: 8 additions & 2 deletions src/librustdoc/passes/strip_private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,25 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
// This stripper collects all *retained* nodes.
let mut retained = ItemIdSet::default();
let is_json_output = cx.output_format.is_json() && !cx.show_coverage;

// strip all private items
{
let mut stripper = Stripper {
retained: &mut retained,
access_levels: &cx.cache.access_levels,
update_retained: true,
is_json_output: cx.output_format.is_json() && !cx.show_coverage,
is_json_output,
};
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
}

// strip all impls referencing private items
let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
let mut stripper = ImplStripper {
retained: &retained,
cache: &cx.cache,
is_json_output,
document_private: cx.render_options.document_private,
};
stripper.fold_crate(krate)
}
53 changes: 39 additions & 14 deletions src/librustdoc/passes/stripper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@ pub(crate) struct Stripper<'a> {
pub(crate) is_json_output: bool,
}

impl<'a> Stripper<'a> {
// We need to handle this differently for the JSON output because some non exported items could
// be used in public API. And so, we need these items as well. `is_exported` only checks if they
// are in the public API, which is not enough.
#[inline]
fn is_item_reachable(&self, item_id: ItemId) -> bool {
if self.is_json_output {
self.access_levels.is_reachable(item_id.expect_def_id())
} else {
self.access_levels.is_exported(item_id.expect_def_id())
}
// We need to handle this differently for the JSON output because some non exported items could
// be used in public API. And so, we need these items as well. `is_exported` only checks if they
// are in the public API, which is not enough.
#[inline]
fn is_item_reachable(
is_json_output: bool,
access_levels: &AccessLevels<DefId>,
item_id: ItemId,
) -> bool {
if is_json_output {
access_levels.is_reachable(item_id.expect_def_id())
} else {
access_levels.is_exported(item_id.expect_def_id())
}
}

Expand Down Expand Up @@ -61,7 +63,9 @@ impl<'a> DocFolder for Stripper<'a> {
| clean::MacroItem(..)
| clean::ForeignTypeItem => {
let item_id = i.item_id;
if item_id.is_local() && !self.is_item_reachable(item_id) {
if item_id.is_local()
&& !is_item_reachable(self.is_json_output, self.access_levels, item_id)
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
}
Expand Down Expand Up @@ -133,15 +137,36 @@ impl<'a> DocFolder for Stripper<'a> {
pub(crate) struct ImplStripper<'a> {
pub(crate) retained: &'a ItemIdSet,
pub(crate) cache: &'a Cache,
pub(crate) is_json_output: bool,
pub(crate) document_private: bool,
}

impl<'a> DocFolder for ImplStripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = *i.kind {
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
// documentation.
if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
return None;
//
// There is one special case: if the impl block contains only private items.
if imp.trait_.is_none() {
// If the only items present are private ones and we're not rendering private items,
// we don't document it.
if !imp.items.is_empty()
&& !self.document_private
&& imp.items.iter().all(|i| {
let item_id = i.item_id;
item_id.is_local()
&& !is_item_reachable(
self.is_json_output,
&self.cache.access_levels,
item_id,
)
})
{
return None;
} else if imp.items.is_empty() && i.doc_value().is_none() {
return None;
}
}
if let Some(did) = imp.for_.def_id(self.cache) {
if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
Expand Down
44 changes: 44 additions & 0 deletions src/test/rustdoc/empty-impl-block-private-with-doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// compile-flags: --document-private-items

#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
#![crate_name = "foo"]

// @has 'foo/struct.Foo.html'
pub struct Foo;

// There are 3 impl blocks with public item and one that should not be displayed
notriddle marked this conversation as resolved.
Show resolved Hide resolved
// by default because it only contains private items (but not in this case because
// we used `--document-private-items`).
// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 4

// Impl block only containing private items should not be displayed unless the
// `--document-private-items` flag is used.
/// Private
impl Foo {
const BAR: u32 = 0;
type FOO = i32;
fn hello() {}
}

// But if any element of the impl block is public, it should be displayed.
/// Not private
impl Foo {
pub const BAR: u32 = 0;
type FOO = i32;
fn hello() {}
}

/// Not private
impl Foo {
const BAR: u32 = 0;
pub type FOO = i32;
fn hello() {}
}

/// Not private
impl Foo {
const BAR: u32 = 0;
type FOO = i32;
pub fn hello() {}
}
40 changes: 40 additions & 0 deletions src/test/rustdoc/empty-impl-block-private.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
#![crate_name = "foo"]

// @has 'foo/struct.Foo.html'
pub struct Foo;

// There are 3 impl blocks with public item and one that should not be displayed
// because it only contains private items.
// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 3

// Impl block only containing private items should not be displayed.
/// Private
impl Foo {
const BAR: u32 = 0;
type FOO = i32;
fn hello() {}
}

// But if any element of the impl block is public, it should be displayed.
/// Not private
impl Foo {
pub const BAR: u32 = 0;
type FOO = i32;
fn hello() {}
}

/// Not private
impl Foo {
const BAR: u32 = 0;
pub type FOO = i32;
fn hello() {}
}

/// Not private
impl Foo {
const BAR: u32 = 0;
type FOO = i32;
pub fn hello() {}
}