diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs
index e186db7052cd0..41a72c083a328 100644
--- a/library/core/src/char/mod.rs
+++ b/library/core/src/char/mod.rs
@@ -34,8 +34,10 @@ pub use self::decode::{DecodeUtf16, DecodeUtf16Error};
// perma-unstable re-exports
#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")]
+#[doc(hidden)]
pub use self::methods::encode_utf16_raw;
#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")]
+#[doc(hidden)]
pub use self::methods::encode_utf8_raw;
use crate::error::Error;
diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs
index 8cc2b7cec4165..9de25153fe7cc 100644
--- a/library/core/src/iter/adapters/mod.rs
+++ b/library/core/src/iter/adapters/mod.rs
@@ -58,9 +58,11 @@ pub use self::intersperse::{Intersperse, IntersperseWith};
pub use self::map_while::MapWhile;
#[unstable(feature = "trusted_random_access", issue = "none")]
+#[doc(hidden)]
pub use self::zip::TrustedRandomAccess;
#[unstable(feature = "trusted_random_access", issue = "none")]
+#[doc(hidden)]
pub use self::zip::TrustedRandomAccessNoCoerce;
#[stable(feature = "iter_zip", since = "1.59.0")]
diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs
index de638552fa378..99fbb613cf27c 100644
--- a/library/core/src/iter/mod.rs
+++ b/library/core/src/iter/mod.rs
@@ -401,6 +401,7 @@ pub use self::sources::{once_with, OnceWith};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::sources::{repeat, Repeat};
#[unstable(feature = "iter_repeat_n", issue = "104434")]
+#[doc(hidden)] // waiting on ACP#120 to decide whether to expose publicly
pub use self::sources::{repeat_n, RepeatN};
#[stable(feature = "iterator_repeat_with", since = "1.28.0")]
pub use self::sources::{repeat_with, RepeatWith};
@@ -410,6 +411,7 @@ pub use self::sources::{successors, Successors};
#[stable(feature = "fused", since = "1.26.0")]
pub use self::traits::FusedIterator;
#[unstable(issue = "none", feature = "inplace_iteration")]
+#[doc(hidden)]
pub use self::traits::InPlaceIterable;
#[unstable(feature = "trusted_len", issue = "37572")]
pub use self::traits::TrustedLen;
@@ -435,12 +437,15 @@ pub use self::adapters::Flatten;
#[stable(feature = "iter_map_while", since = "1.57.0")]
pub use self::adapters::MapWhile;
#[unstable(feature = "inplace_iteration", issue = "none")]
+#[doc(hidden)]
pub use self::adapters::SourceIter;
#[stable(feature = "iterator_step_by", since = "1.28.0")]
pub use self::adapters::StepBy;
#[unstable(feature = "trusted_random_access", issue = "none")]
+#[doc(hidden)]
pub use self::adapters::TrustedRandomAccess;
#[unstable(feature = "trusted_random_access", issue = "none")]
+#[doc(hidden)]
pub use self::adapters::TrustedRandomAccessNoCoerce;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::adapters::{
diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs
index 3ec426a3ad9a1..65ac4c5375244 100644
--- a/library/core/src/iter/sources.rs
+++ b/library/core/src/iter/sources.rs
@@ -18,6 +18,7 @@ pub use self::empty::{empty, Empty};
pub use self::once::{once, Once};
#[unstable(feature = "iter_repeat_n", issue = "104434")]
+#[doc(hidden)]
pub use self::repeat_n::{repeat_n, RepeatN};
#[stable(feature = "iterator_repeat_with", since = "1.28.0")]
diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs
index 41ea29e6a84d9..a788d096a725a 100644
--- a/library/core/src/iter/traits/mod.rs
+++ b/library/core/src/iter/traits/mod.rs
@@ -17,6 +17,7 @@ pub use self::{
};
#[unstable(issue = "none", feature = "inplace_iteration")]
+#[doc(hidden)]
pub use self::marker::InPlaceIterable;
#[unstable(feature = "trusted_step", issue = "85731")]
pub use self::marker::TrustedStep;
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index 97d9b750d92f9..734f50d986c87 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -165,6 +165,7 @@ pub use self::bit::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssig
pub use self::deref::{Deref, DerefMut};
#[unstable(feature = "receiver_trait", issue = "none")]
+#[doc(hidden)]
pub use self::deref::Receiver;
#[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index 10525a16f3a66..4c96afed23b08 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -71,6 +71,7 @@ pub use crate::concat_bytes;
// Do not `doc(inline)` these `doc(hidden)` items.
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[allow(deprecated)]
+#[doc(hidden)]
pub use crate::macros::builtin::{RustcDecodable, RustcEncodable};
// Do not `doc(no_inline)` so that they become doc items on their own
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index ecbf4e66fa489..06e25afff3f35 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -404,6 +404,7 @@ pub use non_null::NonNull;
mod unique;
#[unstable(feature = "ptr_internals", issue = "none")]
+#[doc(hidden)]
pub use unique::Unique;
mod const_ptr;
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index b512135d92770..12a8b2b8db4b6 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -7,6 +7,7 @@
- [How to write documentation](how-to-write-documentation.md)
- [What to include (and exclude)](write-documentation/what-to-include.md)
- [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
+ - [Re-exports](write-documentation/re-exports.md)
- [Linking to items by name](write-documentation/linking-to-items-by-name.md)
- [Documentation tests](write-documentation/documentation-tests.md)
- [Rustdoc-specific lints](lints.md)
diff --git a/src/doc/rustdoc/src/write-documentation/re-exports.md b/src/doc/rustdoc/src/write-documentation/re-exports.md
new file mode 100644
index 0000000000000..480ebd8585f3b
--- /dev/null
+++ b/src/doc/rustdoc/src/write-documentation/re-exports.md
@@ -0,0 +1,145 @@
+# Re-exports
+
+Let's start by explaining what are re-exports. To do so, we will use an example where we are
+writing a library (named `lib`) with some types dispatched in sub-modules:
+
+```rust
+pub mod sub_module1 {
+ pub struct Foo;
+}
+pub mod sub_module2 {
+ pub struct AnotherFoo;
+}
+```
+
+Users can import them like this:
+
+```rust,ignore (inline)
+use lib::sub_module1::Foo;
+use lib::sub_module2::AnotherFoo;
+```
+
+But what if you want the types to be available directly at the crate root or if we don't want the
+modules to be visible for users? That's where re-exports come in:
+
+```rust,ignore (inline)
+// `sub_module1` and `sub_module2` are not visible outside.
+mod sub_module1 {
+ pub struct Foo;
+}
+mod sub_module2 {
+ pub struct AnotherFoo;
+}
+
+// We re-export both types:
+pub use crate::sub_module1::Foo;
+pub use crate::sub_module2::AnotherFoo;
+```
+
+And now users will be able to do:
+
+```rust,ignore (inline)
+use lib::{Foo, AnotherFoo};
+```
+
+And since both `sub_module1` and `sub_module2` are private, users won't be able to import them.
+
+Now what's interesting is that the generated documentation for this crate will show both `Foo` and
+`AnotherFoo` directly at the crate root, meaning they have been inlined. There are a few rules to
+know whether or not a re-exported item will be inlined.
+
+## Inlining rules
+
+If a public item comes from a private module, it will be inlined:
+
+```rust,ignore (inline)
+mod private_module {
+ pub struct Public;
+}
+
+pub mod public_mod {
+ // `Public` will inlined here since `private_module` is private.
+ pub use super::private_module::Public;
+}
+
+// `Public` will not be inlined here since `public_mod` is public.
+pub use self::public_mod::Public;
+```
+
+Likewise, if an item has `#[doc(hidden)]` or inherits it (from any of its parents), it
+will be inlined:
+
+```rust,ignore (inline)
+#[doc(hidden)]
+pub mod public_mod {
+ pub struct Public;
+}
+
+#[doc(hidden)]
+pub struct Hidden;
+
+// `Public` be inlined since its parent (`public_mod`) has `#[doc(hidden)]`.
+pub use self::public_mod::Public;
+// `Hidden` be inlined since it has `#[doc(hidden)]`.
+pub use self::Hidden;
+```
+
+The inlining rules are a bit different for glob re-exports (`pub use x::*`) for `#[doc(hidden)]`
+types. If we take the previous example and then re-export like this:
+
+```rust,ignore (inline)
+pub use self::*; // It will not inline the `Hidden` struct.
+pub use self::public_mod::*; // It will inline the `Public` struct.
+```
+
+It only impacts elements that have the `#[doc(hidden)]` attributes. If it only inherits it, then it
+is inlined.
+
+## Inlining with `#[doc(inline)]`
+
+You can use the `#[doc(inline)]` attribute if you want to force an item to be inlined:
+
+```rust,ignore (inline)
+pub mod public_mod {
+ pub struct Public;
+}
+
+#[doc(inline)]
+pub use self::public_mod::Public;
+```
+
+With this code, even though `public_mod::Public` is public and present in the documentation, the
+`Public` type will be present both at the crate root and in the `public_mod` module.
+
+## Preventing inlining with `#[doc(no_inline)]`
+
+On the opposite of the `#[doc(inline)]` attribute, if you want to prevent an item from being
+inlined, you can use `#[doc(no_inline)]`:
+
+```rust,ignore (inline)
+pub mod public_mod {
+ pub struct Public;
+}
+
+#[doc(no_inline)]
+pub use self::public_mod::Public;
+```
+
+In the generated documentation, you will see a re-export at the crate root and not the type
+directly.
+
+## Attributes
+
+When an item is inlined, its doc comments and most of its attributes will be inlined along with it:
+
+| Attribute | Is it inlined alongside its item? | Notes
+|--|--|--
+| `#[doc=""]` | Yes | Intra-doc links are resolved relative to where the doc comment is defined (`///` is syntax sugar for doc string attributes).
+| `#[doc(cfg(..))]` | Yes |
+| `#[deprecated]` | Yes | Intra-doc links are resolved relative to where the description is defined.
+| `#[doc(alias="")]` | No |
+| `#[doc(inline)]` | No |
+| `#[doc(no_inline)]` | No |
+| `#[doc(hidden)]` | Glob imports | For name-based imports (such as `use module::Item as ModuleItem`), hiding an item acts the same as making it private. For glob-based imports (such as `use module::*`), hidden items are not inlined.
+
+All other attributes are inherited when inlined, so that the documentation matches the behavior if the inlined item was directly defined at the spot where it's shown.
diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
index 8ecf05f0e121f..e444dc030bb7c 100644
--- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
+++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
@@ -223,6 +223,9 @@ Now we'll have a `Re-exports` line, and `Bar` will not link to anywhere.
One special case: In Rust 2018 and later, if you `pub use` one of your dependencies, `rustdoc` will
not eagerly inline it as a module unless you add `#[doc(inline)]`.
+If you want to know more about inlining rules, take a look at the
+[`re-exports` chapter](./re-exports.md).
+
### `hidden`
@@ -230,6 +233,9 @@ not eagerly inline it as a module unless you add `#[doc(inline)]`.
Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless
the `strip-hidden` pass is removed.
+For name-based imports (such as `use module::Item as ModuleItem`), hiding an item acts the same as
+making it private. For glob-based imports (such as `use module::*`), hidden items are not inlined.
+
### `alias`
This attribute adds an alias in the search index.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 59a3e63172406..f3aca032db48e 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -41,7 +41,7 @@ use thin_vec::ThinVec;
use crate::core::{self, DocContext, ImplTraitParam};
use crate::formats::item_type::ItemType;
-use crate::visit_ast::Module as DocModule;
+use crate::visit_ast::{ItemEntry, Module as DocModule};
use utils::*;
@@ -77,7 +77,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
// This covers the case where somebody does an import which should pull in an item,
// but there's already an item with the same namespace and same name. Rust gives
// priority to the not-imported one, so we should, too.
- items.extend(doc.items.values().flat_map(|(item, renamed, import_id)| {
+ items.extend(doc.items.values().flat_map(|ItemEntry { item, renamed, import_id }| {
// First, lower everything other than imports.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
return Vec::new();
@@ -90,7 +90,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
}
v
}));
- items.extend(doc.items.values().flat_map(|(item, renamed, _)| {
+ items.extend(doc.items.values().flat_map(|ItemEntry { item, renamed, .. }| {
// Now we actually lower the imports, skipping everything else.
if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id()));
@@ -133,12 +133,8 @@ fn generate_item_with_correct_attrs(
let def_id = local_def_id.to_def_id();
let target_attrs = inline::load_attrs(cx, def_id);
let attrs = if let Some(import_id) = import_id {
- let is_inline = inline::load_attrs(cx, import_id.to_def_id())
- .lists(sym::doc)
- .get_word_attr(sym::inline)
- .is_some();
- let mut attrs = get_all_import_attributes(cx, import_id, local_def_id, is_inline);
- add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None);
+ let mut attrs = get_all_import_attributes(cx, import_id, local_def_id);
+ add_without_unwanted_attributes(&mut attrs, target_attrs, None);
attrs
} else {
// We only keep the item's attributes.
@@ -2170,7 +2166,6 @@ fn get_all_import_attributes<'hir>(
cx: &mut DocContext<'hir>,
import_def_id: LocalDefId,
target_def_id: LocalDefId,
- is_inline: bool,
) -> Vec<(Cow<'hir, ast::Attribute>, Option)> {
let mut attrs = Vec::new();
let mut first = true;
@@ -2184,7 +2179,7 @@ fn get_all_import_attributes<'hir>(
attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect();
first = false;
} else {
- add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id));
+ add_without_unwanted_attributes(&mut attrs, import_attrs, Some(def_id));
}
}
attrs
@@ -2236,16 +2231,8 @@ fn filter_tokens_from_list(
fn add_without_unwanted_attributes<'hir>(
attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option)>,
new_attrs: &'hir [ast::Attribute],
- is_inline: bool,
import_parent: Option,
) {
- // If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
- if !is_inline {
- for attr in new_attrs {
- attrs.push((Cow::Borrowed(attr), import_parent));
- }
- return;
- }
for attr in new_attrs {
if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
attrs.push((Cow::Borrowed(attr), import_parent));
@@ -2592,7 +2579,8 @@ fn clean_use_statement_inner<'tcx>(
} else {
if inline_attr.is_none()
&& let Res::Def(DefKind::Mod, did) = path.res
- && !did.is_local() && did.is_crate_root()
+ && !did.is_local()
+ && did.is_crate_root()
{
// if we're `pub use`ing an extern crate root, don't inline it unless we
// were specifically asked for it
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 8f8dc6b709053..46a941646cab0 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -19,6 +19,13 @@ use std::{iter, mem};
use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt};
use crate::core;
+#[derive(Debug)]
+pub(crate) struct ItemEntry<'hir> {
+ pub(crate) item: &'hir hir::Item<'hir>,
+ pub(crate) renamed: Option,
+ pub(crate) import_id: Option,
+}
+
/// This module is used to store stuff from Rust's AST in a more convenient
/// manner (and with prettier names) before cleaning.
#[derive(Debug)]
@@ -31,10 +38,7 @@ pub(crate) struct Module<'hir> {
pub(crate) import_id: Option,
/// The key is the item `ItemId` and the value is: (item, renamed, import_id).
/// We use `FxIndexMap` to keep the insert order.
- pub(crate) items: FxIndexMap<
- (LocalDefId, Option),
- (&'hir hir::Item<'hir>, Option, Option),
- >,
+ pub(crate) items: FxIndexMap<(LocalDefId, Option), ItemEntry<'hir>>,
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option)>,
}
@@ -107,6 +111,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> {
modules: Vec>,
is_importable_from_parent: bool,
inside_body: bool,
+ ignored_items: usize,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
@@ -131,6 +136,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
modules: vec![om],
is_importable_from_parent: true,
inside_body: false,
+ ignored_items: 0,
}
}
@@ -165,7 +171,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
inserted.insert(def_id)
{
let item = self.cx.tcx.hir().expect_item(local_def_id);
- top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None));
+ top_level_module.items.insert((local_def_id, Some(item.ident.name)), ItemEntry {
+ item,
+ renamed: None,
+ import_id: None,
+ });
}
}
@@ -263,9 +273,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
};
let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
- // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
- let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
- || use_attrs.lists(sym::doc).has_word(sym::hidden);
+ let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline);
// For cross-crate impl inlining we need to know whether items are
// reachable in documentation -- a previously unreachable item can be
@@ -280,12 +288,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
};
- let is_private =
- !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, ori_res_did);
- let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did, None);
+ let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did);
+ let is_hidden = tcx.is_doc_hidden(res_did) || inherits_doc_hidden(tcx, res_did, None);
// Only inline if requested or if the item would otherwise be stripped.
- if (!please_inline && !is_private && !is_hidden) || is_no_inline {
+ if is_no_inline || (!please_inline && !is_private && !is_hidden) {
return false;
}
@@ -298,8 +305,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
.cx
.cache
.effective_visibilities
- .is_directly_public(self.cx.tcx, item_def_id.to_def_id()) &&
- !inherits_doc_hidden(self.cx.tcx, item_def_id, None)
+ .is_directly_public(tcx, item_def_id.to_def_id()) &&
+ !tcx.is_doc_hidden(item_def_id) &&
+ !inherits_doc_hidden(tcx, item_def_id, None)
{
// The imported item is public and not `doc(hidden)` so no need to inline it.
return false;
@@ -312,22 +320,25 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
let ret = match tcx.hir().get_by_def_id(res_did) {
Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
let prev = mem::replace(&mut self.inlining, true);
+ let prev_ignored = self.ignored_items;
for &i in m.item_ids {
- let i = self.cx.tcx.hir().item(i);
- self.visit_item_inner(i, None, Some(def_id));
+ let i = tcx.hir().item(i);
+ self.visit_item_inner(i, None, Some(def_id), true);
}
self.inlining = prev;
- true
+ let inline = self.ignored_items - prev_ignored == 0;
+ self.ignored_items = prev_ignored;
+ inline
}
Node::Item(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_item_inner(it, renamed, Some(def_id));
+ self.visit_item_inner(it, renamed, Some(def_id), false);
self.inlining = prev;
true
}
Node::ForeignItem(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_foreign_item_inner(it, renamed);
+ self.visit_foreign_item_inner(it, renamed, false);
self.inlining = prev;
true
}
@@ -343,6 +354,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
item: &'tcx hir::Item<'_>,
renamed: Option,
parent_id: Option,
+ from_glob_reexport: bool,
) {
if self.is_importable_from_parent
// If we're inside an item, only impl blocks and `macro_rules!` with the `macro_export`
@@ -355,11 +367,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
_ => false,
}
{
- self.modules
- .last_mut()
- .unwrap()
- .items
- .insert((item.owner_id.def_id, renamed), (item, renamed, parent_id));
+ if !from_glob_reexport || !self.cx.tcx.is_doc_hidden(item.owner_id.to_def_id()) {
+ self.modules.last_mut().unwrap().items.insert(
+ (item.owner_id.def_id, renamed),
+ ItemEntry { item, renamed, import_id: parent_id },
+ );
+ } else {
+ self.ignored_items += 1;
+ }
}
}
@@ -368,6 +383,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
item: &'tcx hir::Item<'_>,
renamed: Option,
import_id: Option,
+ from_glob_reexport: bool,
) {
debug!("visiting item {:?}", item);
if self.inside_body {
@@ -386,7 +402,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// them up regardless of where they're located.
impl_.of_trait.is_none()
{
- self.add_to_current_mod(item, None, None);
+ self.add_to_current_mod(item, None, None, false);
}
return;
}
@@ -404,7 +420,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::ItemKind::ForeignMod { items, .. } => {
for item in items {
let item = tcx.hir().foreign_item(item.id);
- self.visit_foreign_item_inner(item, None);
+ self.visit_foreign_item_inner(item, None, from_glob_reexport);
}
}
// If we're inlining, skip private items.
@@ -419,6 +435,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
continue;
}
+ if from_glob_reexport &&
+ tcx.is_doc_hidden(def_id) &&
+ let Some(res_def_id) = res.opt_def_id() &&
+ tcx.is_doc_hidden(res_def_id)
+ {
+ self.ignored_items += 1;
+ continue;
+ }
+
let attrs =
tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(item.owner_id.def_id));
@@ -444,7 +469,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
}
- self.add_to_current_mod(item, renamed, import_id);
+ self.add_to_current_mod(item, renamed, import_id, false);
}
}
hir::ItemKind::Macro(ref macro_def, _) => {
@@ -464,7 +489,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
let nonexported = !tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
- self.add_to_current_mod(item, renamed, import_id);
+ self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
}
}
hir::ItemKind::Mod(ref m) => {
@@ -483,7 +508,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
| hir::ItemKind::TraitAlias(..) => {
- self.add_to_current_mod(item, renamed, import_id);
+ self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
}
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(_) | hir::OpaqueTyOrigin::FnReturn(_),
@@ -495,14 +520,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if name != kw::Underscore {
- self.add_to_current_mod(item, renamed, import_id);
+ self.add_to_current_mod(item, renamed, import_id, from_glob_reexport);
}
}
hir::ItemKind::Impl(impl_) => {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && impl_.of_trait.is_none() {
- self.add_to_current_mod(item, None, None);
+ self.add_to_current_mod(item, None, None, false);
}
}
}
@@ -512,10 +537,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
&mut self,
item: &'tcx hir::ForeignItem<'_>,
renamed: Option,
+ from_glob_reexport: bool,
) {
// If inlining we only want to include public functions.
if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() {
- self.modules.last_mut().unwrap().foreigns.push((item, renamed));
+ if !from_glob_reexport || !self.cx.tcx.is_doc_hidden(item.owner_id.to_def_id()) {
+ self.modules.last_mut().unwrap().foreigns.push((item, renamed));
+ } else {
+ self.ignored_items += 1;
+ }
}
}
@@ -549,7 +579,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
}
fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
- self.visit_item_inner(i, None, None);
+ self.visit_item_inner(i, None, None, false);
let new_value = self.is_importable_from_parent
&& matches!(
i.kind,
diff --git a/tests/rustdoc/doc-hidden-reexports.rs b/tests/rustdoc/doc-hidden-reexports.rs
new file mode 100644
index 0000000000000..6978fee1cfe9a
--- /dev/null
+++ b/tests/rustdoc/doc-hidden-reexports.rs
@@ -0,0 +1,114 @@
+// Test to enforce rules over re-exports inlining from
+// .
+
+#![crate_name = "foo"]
+
+mod private_module {
+ #[doc(hidden)]
+ pub struct Public;
+ #[doc(hidden)]
+ pub type Bar = ();
+}
+
+#[doc(hidden)]
+mod module {
+ pub struct Public2;
+ pub type Bar2 = ();
+}
+
+#[doc(hidden)]
+pub type Bar3 = ();
+#[doc(hidden)]
+pub struct FooFoo;
+
+// Checking that re-exporting a `#[doc(hidden)]` item will inline it.
+pub mod single_reexport {
+ // @has 'foo/single_reexport/index.html'
+
+ // First we check that we have all 6 items: 3 structs and 3 type aliases.
+ // @count - '//a[@class="struct"]' 3
+ // @count - '//a[@class="type"]' 3
+
+ // Then we check that we have the correct link for each re-export.
+
+ // @has - '//*[@href="struct.Foo.html"]' 'Foo'
+ pub use crate::private_module::Public as Foo;
+ // @has - '//*[@href="type.Foo2.html"]' 'Foo2'
+ pub use crate::private_module::Bar as Foo2;
+ // @has - '//*[@href="type.Yo.html"]' 'Yo'
+ pub use crate::Bar3 as Yo;
+ // @has - '//*[@href="struct.Yo2.html"]' 'Yo2'
+ pub use crate::FooFoo as Yo2;
+ // @has - '//*[@href="struct.Foo3.html"]' 'Foo3'
+ pub use crate::module::Public2 as Foo3;
+ // @has - '//*[@href="type.Foo4.html"]' 'Foo4'
+ pub use crate::module::Bar2 as Foo4;
+
+ // Checking that each file is also created as expected.
+ // @has 'foo/single_reexport/struct.Foo.html'
+ // @has 'foo/single_reexport/type.Foo2.html'
+ // @has 'foo/single_reexport/type.Yo.html'
+ // @has 'foo/single_reexport/struct.Yo2.html'
+ // @has 'foo/single_reexport/struct.Foo3.html'
+ // @has 'foo/single_reexport/type.Foo4.html'
+}
+
+// Checking that re-exporting a `#[doc(hidden)]` item with `#[doc(no_inline)]` will not inline
+// it but will still display a re-export in the documentation.
+pub mod single_reexport_no_inline {
+ // First we ensure that we only have re-exports and no inlined items.
+ // @has 'foo/single_reexport_no_inline/index.html'
+ // @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 1
+ // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
+
+ // Now we check that we don't have links to the items, just `pub use`.
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Public as XFoo;'
+ // @!has - '//*[@id="main-content"]//a' 'XFoo'
+ #[doc(no_inline)]
+ pub use crate::private_module::Public as XFoo;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Bar as Foo2;'
+ // @!has - '//*[@id="main-content"]//a' 'Foo2'
+ #[doc(no_inline)]
+ pub use crate::private_module::Bar as Foo2;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::Bar3 as Yo;'
+ // @!has - '//*[@id="main-content"]//a' 'Yo'
+ #[doc(no_inline)]
+ pub use crate::Bar3 as Yo;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::FooFoo as Yo2;'
+ // @!has - '//*[@id="main-content"]//a' 'Yo2'
+ #[doc(no_inline)]
+ pub use crate::FooFoo as Yo2;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::module::Public2 as Foo3;'
+ // @!has - '//*[@id="main-content"]//a' 'Foo3'
+ #[doc(no_inline)]
+ pub use crate::module::Public2 as Foo3;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::module::Bar2 as Foo4;'
+ // @!has - '//*[@id="main-content"]//a' 'Foo4'
+ #[doc(no_inline)]
+ pub use crate::module::Bar2 as Foo4;
+}
+
+// Checking that glob re-exports don't inline `#[doc(hidden)]` items.
+pub mod glob_reexport {
+ // With glob re-exports, we don't inline `#[doc(hidden)]` items so only `module` items
+ // should be inlined.
+ // @has 'foo/glob_reexport/index.html'
+ // @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 3
+ // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
+ // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs'
+ // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions'
+
+ // Now we check we have 2 re-exports and 2 inlined items.
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::*;'
+ pub use crate::private_module::*;
+ // @has - '//*[@id="main-content"]//*' 'pub use crate::*;'
+ pub use crate::*;
+ // This one should be inlined.
+ // @!has - '//*[@id="main-content"]//*' 'pub use crate::module::*;'
+ // @has - '//*[@id="main-content"]//a[@href="struct.Public2.html"]' 'Public2'
+ // @has - '//*[@id="main-content"]//a[@href="type.Bar2.html"]' 'Bar2'
+ // And we check that the two files were created too.
+ // @has 'foo/glob_reexport/struct.Public2.html'
+ // @has 'foo/glob_reexport/type.Bar2.html'
+ pub use crate::module::*;
+}
diff --git a/tests/rustdoc/reexport-attr-merge.rs b/tests/rustdoc/reexport-attr-merge.rs
index f6c23a1365f46..5300dc4324da6 100644
--- a/tests/rustdoc/reexport-attr-merge.rs
+++ b/tests/rustdoc/reexport-attr-merge.rs
@@ -4,6 +4,8 @@
#![crate_name = "foo"]
#![feature(doc_cfg)]
+#![feature(no_core)]
+#![no_core]
// @has 'foo/index.html'
@@ -20,14 +22,13 @@ pub use Foo1 as Foo2;
// are inlined.
// @count - '//a[@class="struct"]' 2
// Then we check that both `cfg` are displayed.
-// @has - '//*[@class="stab portability"]' 'foo'
-// @has - '//*[@class="stab portability"]' 'bar'
+// @matches - '//*[@class="stab portability"]' '^foo$'
// And finally we check that the only element displayed is `Bar`.
-// @has - '//a[@class="struct"]' 'Bar'
+// @has - '//a[@href="struct.Bar.html"]' 'Bar'
#[doc(inline)]
-pub use Foo2 as Bar;
+pub use Foo as Bar;
-// This one should appear but `Bar2` won't be linked because there is no
-// `#[doc(inline)]`.
-// @has - '//*[@id="reexport.Bar2"]' 'pub use Foo2 as Bar2;'
+// Re-exported `#[doc(hidden)]` items are inlined as well.
+// @has - '//a[@href="struct.Bar2.html"]' 'Bar2'
+// @matches - '//*[@class="stab portability"]' '^bar and foo$'
pub use Foo2 as Bar2;
diff --git a/tests/rustdoc/reexport-doc-hidden.rs b/tests/rustdoc/reexport-doc-hidden.rs
deleted file mode 100644
index 3ea5fde72f711..0000000000000
--- a/tests/rustdoc/reexport-doc-hidden.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Part of .
-// This test ensures that reexporting a `doc(hidden)` item will
-// still show the reexport.
-
-#![crate_name = "foo"]
-
-#[doc(hidden)]
-pub type Type = u32;
-
-// @has 'foo/index.html'
-// @has - '//*[@id="reexport.Type2"]/code' 'pub use crate::Type as Type2;'
-pub use crate::Type as Type2;
-
-// @count - '//*[@id="reexport.Type3"]' 0
-#[doc(hidden)]
-pub use crate::Type as Type3;
-
-#[macro_export]
-#[doc(hidden)]
-macro_rules! foo {
- () => {};
-}
-
-// This is a bug: https://github.com/rust-lang/rust/issues/59368
-// @!has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;'
-pub use crate::foo as Macro;