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..593428b8a70a5 --- /dev/null +++ b/src/doc/rustdoc/src/write-documentation/re-exports.md @@ -0,0 +1,172 @@ +# 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 inherits `#[doc(hidden)]` from any of its ancestors, it will be inlined: + +```rust,ignore (inline) +#[doc(hidden)] +pub mod public_mod { + pub struct Public; +} +// `Public` be inlined since its parent (`public_mod`) has `#[doc(hidden)]`. +pub use self::public_mod::Public; +``` + +If an item has `#[doc(hidden)]`, it won't be inlined (nor visible in the generated documentation): + +```rust,ignore (inline) +// This struct won't be visible. +#[doc(hidden)] +pub struct Hidden; + +// This re-export won't be visible. +pub use self::Hidden as InlinedHidden; +``` + +The same applies on re-exports themselves: if you have multiple re-exports and some of them have +`#[doc(hidden)]`, then these ones (and only these) own't appear in the documentation: + +```rust,ignore (inline) +mod private_mod { + /// First + pub struct InPrivate; +} + +/// Second +#[doc(hidden)] +pub use self::private_mod::InPrivate as Hidden; +/// Third +pub use self::Hidden as Visible; +``` + +In this case, `InPrivate` will be inlined as `Visible`. However, its documentation will be +`First Third` and not `First Second Third` because the re-export with `Second` as documentation has +`#[doc(hidden)]`, therefore, all its attributes are ignored. + +## 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) +mod private_mod { + pub struct Public; +} +#[doc(no_inline)] +pub use self::private_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: + +```rust,ignore (inline) +mod private_mod { + /// First + #[cfg(a)] + pub struct InPrivate; + /// Second + #[cfg(b)] + pub use self::InPrivate as Second; +} + +/// Third +#[doc(inline)] +#[cfg(c)] +pub use self::private_mod::Second as Visible; +``` + +In this case, `Visible` will have as documentation `First Second Third` and will also have as `cfg`: +`#[cfg(a, b, c)]`. + +[Intra-doc links](./linking-to-items-by-name.md) are resolved relative to where the doc comment is +defined. + +There are a few attributes which are not inlined though: + * `#[doc(alias="")]` + * `#[doc(inline)]` + * `#[doc(no_inline)]` + * `#[doc(hidden)]` (because the re-export itself and its attributes are ignored). + +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..046d018543f38 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -223,12 +223,18 @@ 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` Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless -the `strip-hidden` pass is removed. +the `strip-hidden` pass is removed. Re-exported items where one of its ancestors has +`#[doc(hidden)]` will be considered the same as private. + +You can find more information in the [`re-exports` chapter](./re-exports.md). ### `alias` diff --git a/tests/rustdoc/issue-109449-doc-hidden-reexports.rs b/tests/rustdoc/issue-109449-doc-hidden-reexports.rs new file mode 100644 index 0000000000000..b0c2254018048 --- /dev/null +++ b/tests/rustdoc/issue-109449-doc-hidden-reexports.rs @@ -0,0 +1,143 @@ +// 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 NOT inline it. +pub mod single_reexport { + // @has 'foo/single_reexport/index.html' + + // First we check that we have 4 type aliases. + // @count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4 + + // Then we check that we have the correct link for each re-export. + + // @!has - '//*[@href="struct.Foo.html"]' 'Foo' + // @has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' + pub use crate::private_module::Public as Foo; + // @!has - '//*[@href="type.Foo2.html"]' 'Foo2' + // @has - '//*[@id="reexport.Foo2"]/code' 'pub use crate::private_module::Bar as Foo2;' + pub use crate::private_module::Bar as Foo2; + // @!has - '//*[@href="type.Yo.html"]' 'Yo' + // @has - '//*[@id="reexport.Yo"]/code' 'pub use crate::Bar3 as Yo;' + pub use crate::Bar3 as Yo; + // @!has - '//*[@href="struct.Yo2.html"]' 'Yo2' + // @has - '//*[@id="reexport.Yo2"]/code' 'pub use crate::FooFoo as Yo2;' + pub use crate::FooFoo as Yo2; + + // 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' +} + +// However, re-exporting an item inheriting `#[doc(hidden)]` will inline it. +pub mod single_reexport_inherit_hidden { + // @has 'foo/single_reexport_inherit_hidden/index.html' + + // @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; + + // @has 'foo/single_reexport_inherit_hidden/struct.Foo3.html' + // @has 'foo/single_reexport_inherit_hidden/type.Foo4.html' +} + +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 1 re-export and 2 inlined items. + // If not item from a glob re-export is visible, we don't show the re-export. + // @!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::*; +} + +mod private { + /// Original. + pub struct Bar3; +} + +// Checking that `#[doc(hidden)]` re-exports documentation isn't generated. +pub mod doc_hidden_reexport { + // @has 'foo/doc_hidden_reexport/index.html' + // Ensure there is only one item in this page and that it's a struct. + // @count - '//*[@class="item-name"]' 1 + // @has - '//a[@class="struct"]' 'Reexport' + // Check that the `#[doc(hidden)]` re-export's attributes are not taken into account. + // @has - '//*[@class="desc docblock-short"]' 'Visible. Original.' + /// Hidden. + #[doc(hidden)] + pub use crate::private::Bar3; + /// Visible. + pub use self::Bar3 as Reexport; +}