Skip to content

Commit 9bc95a4

Browse files
authored
Rollup merge of #112304 - GuillaumeGomez:re-exports, r=notriddle
Add chapter in rustdoc book for re-exports and add a regression test for `#[doc(hidden)]` behaviour Fixes #109449. Fixes #53417. After the discussion in #109697, I made a few PRs to fix a few corner cases: * #112178 * #112108 * #111997 With this I think I covered all cases. Only thing missing at this point was a chapter covering re-exports in the rustdoc book. r? `@notriddle`
2 parents f530016 + 44b1365 commit 9bc95a4

File tree

4 files changed

+323
-1
lines changed

4 files changed

+323
-1
lines changed

src/doc/rustdoc/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [How to write documentation](how-to-write-documentation.md)
88
- [What to include (and exclude)](write-documentation/what-to-include.md)
99
- [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
10+
- [Re-exports](write-documentation/re-exports.md)
1011
- [Linking to items by name](write-documentation/linking-to-items-by-name.md)
1112
- [Documentation tests](write-documentation/documentation-tests.md)
1213
- [Rustdoc-specific lints](lints.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Re-exports
2+
3+
Let's start by explaining what are re-exports. To do so, we will use an example where we are
4+
writing a library (named `lib`) with some types dispatched in sub-modules:
5+
6+
```rust
7+
pub mod sub_module1 {
8+
pub struct Foo;
9+
}
10+
pub mod sub_module2 {
11+
pub struct AnotherFoo;
12+
}
13+
```
14+
15+
Users can import them like this:
16+
17+
```rust,ignore (inline)
18+
use lib::sub_module1::Foo;
19+
use lib::sub_module2::AnotherFoo;
20+
```
21+
22+
But what if you want the types to be available directly at the crate root or if we don't want the
23+
modules to be visible for users? That's where re-exports come in:
24+
25+
```rust,ignore (inline)
26+
// `sub_module1` and `sub_module2` are not visible outside.
27+
mod sub_module1 {
28+
pub struct Foo;
29+
}
30+
mod sub_module2 {
31+
pub struct AnotherFoo;
32+
}
33+
// We re-export both types:
34+
pub use crate::sub_module1::Foo;
35+
pub use crate::sub_module2::AnotherFoo;
36+
```
37+
38+
And now users will be able to do:
39+
40+
```rust,ignore (inline)
41+
use lib::{Foo, AnotherFoo};
42+
```
43+
44+
And since both `sub_module1` and `sub_module2` are private, users won't be able to import them.
45+
46+
Now what's interesting is that the generated documentation for this crate will show both `Foo` and
47+
`AnotherFoo` directly at the crate root, meaning they have been inlined. There are a few rules to
48+
know whether or not a re-exported item will be inlined.
49+
50+
## Inlining rules
51+
52+
If a public item comes from a private module, it will be inlined:
53+
54+
```rust,ignore (inline)
55+
mod private_module {
56+
pub struct Public;
57+
}
58+
pub mod public_mod {
59+
// `Public` will inlined here since `private_module` is private.
60+
pub use super::private_module::Public;
61+
}
62+
// `Public` will not be inlined here since `public_mod` is public.
63+
pub use self::public_mod::Public;
64+
```
65+
66+
Likewise, if an item inherits `#[doc(hidden)]` from any of its ancestors, it will be inlined:
67+
68+
```rust,ignore (inline)
69+
#[doc(hidden)]
70+
pub mod public_mod {
71+
pub struct Public;
72+
}
73+
// `Public` be inlined since its parent (`public_mod`) has `#[doc(hidden)]`.
74+
pub use self::public_mod::Public;
75+
```
76+
77+
If an item has `#[doc(hidden)]`, it won't be inlined (nor visible in the generated documentation):
78+
79+
```rust,ignore (inline)
80+
// This struct won't be visible.
81+
#[doc(hidden)]
82+
pub struct Hidden;
83+
84+
// This re-export won't be visible.
85+
pub use self::Hidden as InlinedHidden;
86+
```
87+
88+
The same applies on re-exports themselves: if you have multiple re-exports and some of them have
89+
`#[doc(hidden)]`, then these ones (and only these) own't appear in the documentation:
90+
91+
```rust,ignore (inline)
92+
mod private_mod {
93+
/// First
94+
pub struct InPrivate;
95+
}
96+
97+
/// Second
98+
#[doc(hidden)]
99+
pub use self::private_mod::InPrivate as Hidden;
100+
/// Third
101+
pub use self::Hidden as Visible;
102+
```
103+
104+
In this case, `InPrivate` will be inlined as `Visible`. However, its documentation will be
105+
`First Third` and not `First Second Third` because the re-export with `Second` as documentation has
106+
`#[doc(hidden)]`, therefore, all its attributes are ignored.
107+
108+
## Inlining with `#[doc(inline)]`
109+
110+
You can use the `#[doc(inline)]` attribute if you want to force an item to be inlined:
111+
112+
```rust,ignore (inline)
113+
pub mod public_mod {
114+
pub struct Public;
115+
}
116+
#[doc(inline)]
117+
pub use self::public_mod::Public;
118+
```
119+
120+
With this code, even though `public_mod::Public` is public and present in the documentation, the
121+
`Public` type will be present both at the crate root and in the `public_mod` module.
122+
123+
## Preventing inlining with `#[doc(no_inline)]`
124+
125+
On the opposite of the `#[doc(inline)]` attribute, if you want to prevent an item from being
126+
inlined, you can use `#[doc(no_inline)]`:
127+
128+
```rust,ignore (inline)
129+
mod private_mod {
130+
pub struct Public;
131+
}
132+
#[doc(no_inline)]
133+
pub use self::private_mod::Public;
134+
```
135+
136+
In the generated documentation, you will see a re-export at the crate root and not the type
137+
directly.
138+
139+
## Attributes
140+
141+
When an item is inlined, its doc comments and most of its attributes will be inlined along with it:
142+
143+
```rust,ignore (inline)
144+
mod private_mod {
145+
/// First
146+
#[cfg(a)]
147+
pub struct InPrivate;
148+
/// Second
149+
#[cfg(b)]
150+
pub use self::InPrivate as Second;
151+
}
152+
153+
/// Third
154+
#[doc(inline)]
155+
#[cfg(c)]
156+
pub use self::private_mod::Second as Visible;
157+
```
158+
159+
In this case, `Visible` will have as documentation `First Second Third` and will also have as `cfg`:
160+
`#[cfg(a, b, c)]`.
161+
162+
[Intra-doc links](./linking-to-items-by-name.md) are resolved relative to where the doc comment is
163+
defined.
164+
165+
There are a few attributes which are not inlined though:
166+
* `#[doc(alias="")]`
167+
* `#[doc(inline)]`
168+
* `#[doc(no_inline)]`
169+
* `#[doc(hidden)]` (because the re-export itself and its attributes are ignored).
170+
171+
All other attributes are inherited when inlined, so that the documentation matches the behavior if
172+
the inlined item was directly defined at the spot where it's shown.

src/doc/rustdoc/src/write-documentation/the-doc-attribute.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,18 @@ Now we'll have a `Re-exports` line, and `Bar` will not link to anywhere.
223223
One special case: In Rust 2018 and later, if you `pub use` one of your dependencies, `rustdoc` will
224224
not eagerly inline it as a module unless you add `#[doc(inline)]`.
225225

226+
If you want to know more about inlining rules, take a look at the
227+
[`re-exports` chapter](./re-exports.md).
228+
226229
### `hidden`
227230

228231
<span id="dochidden"></span>
229232

230233
Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless
231-
the `strip-hidden` pass is removed.
234+
the `strip-hidden` pass is removed. Re-exported items where one of its ancestors has
235+
`#[doc(hidden)]` will be considered the same as private.
236+
237+
You can find more information in the [`re-exports` chapter](./re-exports.md).
232238

233239
### `alias`
234240

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Test to enforce rules over re-exports inlining from
2+
// <https://github.com/rust-lang/rust/issues/109449>.
3+
4+
#![crate_name = "foo"]
5+
6+
mod private_module {
7+
#[doc(hidden)]
8+
pub struct Public;
9+
#[doc(hidden)]
10+
pub type Bar = ();
11+
}
12+
13+
#[doc(hidden)]
14+
mod module {
15+
pub struct Public2;
16+
pub type Bar2 = ();
17+
}
18+
19+
#[doc(hidden)]
20+
pub type Bar3 = ();
21+
#[doc(hidden)]
22+
pub struct FooFoo;
23+
24+
// Checking that re-exporting a `#[doc(hidden)]` item will NOT inline it.
25+
pub mod single_reexport {
26+
// @has 'foo/single_reexport/index.html'
27+
28+
// First we check that we have 4 type aliases.
29+
// @count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4
30+
31+
// Then we check that we have the correct link for each re-export.
32+
33+
// @!has - '//*[@href="struct.Foo.html"]' 'Foo'
34+
// @has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;'
35+
pub use crate::private_module::Public as Foo;
36+
// @!has - '//*[@href="type.Foo2.html"]' 'Foo2'
37+
// @has - '//*[@id="reexport.Foo2"]/code' 'pub use crate::private_module::Bar as Foo2;'
38+
pub use crate::private_module::Bar as Foo2;
39+
// @!has - '//*[@href="type.Yo.html"]' 'Yo'
40+
// @has - '//*[@id="reexport.Yo"]/code' 'pub use crate::Bar3 as Yo;'
41+
pub use crate::Bar3 as Yo;
42+
// @!has - '//*[@href="struct.Yo2.html"]' 'Yo2'
43+
// @has - '//*[@id="reexport.Yo2"]/code' 'pub use crate::FooFoo as Yo2;'
44+
pub use crate::FooFoo as Yo2;
45+
46+
// Checking that each file is also created as expected.
47+
// @!has 'foo/single_reexport/struct.Foo.html'
48+
// @!has 'foo/single_reexport/type.Foo2.html'
49+
// @!has 'foo/single_reexport/type.Yo.html'
50+
// @!has 'foo/single_reexport/struct.Yo2.html'
51+
}
52+
53+
// However, re-exporting an item inheriting `#[doc(hidden)]` will inline it.
54+
pub mod single_reexport_inherit_hidden {
55+
// @has 'foo/single_reexport_inherit_hidden/index.html'
56+
57+
// @has - '//*[@href="struct.Foo3.html"]' 'Foo3'
58+
pub use crate::module::Public2 as Foo3;
59+
// @has - '//*[@href="type.Foo4.html"]' 'Foo4'
60+
pub use crate::module::Bar2 as Foo4;
61+
62+
// @has 'foo/single_reexport_inherit_hidden/struct.Foo3.html'
63+
// @has 'foo/single_reexport_inherit_hidden/type.Foo4.html'
64+
}
65+
66+
pub mod single_reexport_no_inline {
67+
// First we ensure that we only have re-exports and no inlined items.
68+
// @has 'foo/single_reexport_no_inline/index.html'
69+
// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 1
70+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
71+
72+
// Now we check that we don't have links to the items, just `pub use`.
73+
// @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Public as XFoo;'
74+
// @!has - '//*[@id="main-content"]//a' 'XFoo'
75+
#[doc(no_inline)]
76+
pub use crate::private_module::Public as XFoo;
77+
// @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Bar as Foo2;'
78+
// @!has - '//*[@id="main-content"]//a' 'Foo2'
79+
#[doc(no_inline)]
80+
pub use crate::private_module::Bar as Foo2;
81+
// @has - '//*[@id="main-content"]//*' 'pub use crate::Bar3 as Yo;'
82+
// @!has - '//*[@id="main-content"]//a' 'Yo'
83+
#[doc(no_inline)]
84+
pub use crate::Bar3 as Yo;
85+
// @has - '//*[@id="main-content"]//*' 'pub use crate::FooFoo as Yo2;'
86+
// @!has - '//*[@id="main-content"]//a' 'Yo2'
87+
#[doc(no_inline)]
88+
pub use crate::FooFoo as Yo2;
89+
// @has - '//*[@id="main-content"]//*' 'pub use crate::module::Public2 as Foo3;'
90+
// @!has - '//*[@id="main-content"]//a' 'Foo3'
91+
#[doc(no_inline)]
92+
pub use crate::module::Public2 as Foo3;
93+
// @has - '//*[@id="main-content"]//*' 'pub use crate::module::Bar2 as Foo4;'
94+
// @!has - '//*[@id="main-content"]//a' 'Foo4'
95+
#[doc(no_inline)]
96+
pub use crate::module::Bar2 as Foo4;
97+
}
98+
99+
// Checking that glob re-exports don't inline `#[doc(hidden)]` items.
100+
pub mod glob_reexport {
101+
// With glob re-exports, we don't inline `#[doc(hidden)]` items so only `module` items
102+
// should be inlined.
103+
// @has 'foo/glob_reexport/index.html'
104+
// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 3
105+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
106+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs'
107+
// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions'
108+
109+
// Now we check we have 1 re-export and 2 inlined items.
110+
// If not item from a glob re-export is visible, we don't show the re-export.
111+
// @!has - '//*[@id="main-content"]//*' 'pub use crate::private_module::*;'
112+
pub use crate::private_module::*;
113+
// @has - '//*[@id="main-content"]//*' 'pub use crate::*;'
114+
pub use crate::*;
115+
// This one should be inlined.
116+
// @!has - '//*[@id="main-content"]//*' 'pub use crate::module::*;'
117+
// @has - '//*[@id="main-content"]//a[@href="struct.Public2.html"]' 'Public2'
118+
// @has - '//*[@id="main-content"]//a[@href="type.Bar2.html"]' 'Bar2'
119+
// And we check that the two files were created too.
120+
// @has 'foo/glob_reexport/struct.Public2.html'
121+
// @has 'foo/glob_reexport/type.Bar2.html'
122+
pub use crate::module::*;
123+
}
124+
125+
mod private {
126+
/// Original.
127+
pub struct Bar3;
128+
}
129+
130+
// Checking that `#[doc(hidden)]` re-exports documentation isn't generated.
131+
pub mod doc_hidden_reexport {
132+
// @has 'foo/doc_hidden_reexport/index.html'
133+
// Ensure there is only one item in this page and that it's a struct.
134+
// @count - '//*[@class="item-name"]' 1
135+
// @has - '//a[@class="struct"]' 'Reexport'
136+
// Check that the `#[doc(hidden)]` re-export's attributes are not taken into account.
137+
// @has - '//*[@class="desc docblock-short"]' 'Visible. Original.'
138+
/// Hidden.
139+
#[doc(hidden)]
140+
pub use crate::private::Bar3;
141+
/// Visible.
142+
pub use self::Bar3 as Reexport;
143+
}

0 commit comments

Comments
 (0)