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

Unify behaviours for re-exports of #[doc(hidden)] #109697

Closed
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
2 changes: 2 additions & 0 deletions library/core/src/char/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
5 changes: 5 additions & 0 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand All @@ -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::{
Expand Down
1 change: 1 addition & 0 deletions library/core/src/iter/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/iter/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/prelude/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/doc/rustdoc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
145 changes: 145 additions & 0 deletions src/doc/rustdoc/src/write-documentation/re-exports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Re-exports
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shoudl link to this page in the docs for inline/no_inline


Let's start by explaining what are re-exports. To do so, we will use an example where we are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have time to do this myself but would someone be able to proofread this for grammar?

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.
notriddle marked this conversation as resolved.
Show resolved Hide resolved

## Inlining with `#[doc(inline)]`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also explain no_inline in this file


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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this section is hard to understand. It talks about whether "attributes are inlined" but it's more about attributes whose effects are inlined, or if they are themselves effecting inlining.

@notriddle do you have any ideas on how to better frame this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc(hidden) / glob import special case is already documented earlier in this chapter. They get mentioned here purely for completeness's sake.

The docs for doc(alias) are the big thing that isn't mentioned anywhere else that I can find. If this was added to the doc(alias) chapter, and a mention of inlining was added to the doc(hidden) chapter, then this section could be removed (or simplified to a list of links pointing at the applicable subchapter that describes how each attribute gets inlined).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just think that the table below is heterogenous, with the "inlined?" column not quite making sense in a consistent way across entries.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced "Inlined?" with "Is it inlined alongside its item?".


| 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.
6 changes: 6 additions & 0 deletions src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,19 @@ 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`

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

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.
Expand Down
28 changes: 8 additions & 20 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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();
Expand All @@ -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()));
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<DefId>)> {
let mut attrs = Vec::new();
let mut first = true;
Expand All @@ -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
Expand Down Expand Up @@ -2236,16 +2231,8 @@ fn filter_tokens_from_list(
fn add_without_unwanted_attributes<'hir>(
attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)>,
new_attrs: &'hir [ast::Attribute],
is_inline: bool,
import_parent: Option<DefId>,
) {
// 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));
Expand Down Expand Up @@ -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
Expand Down
Loading