Skip to content

Commit

Permalink
Use #[doc(fake_variadic)] to improve docs readability (#14703)
Browse files Browse the repository at this point in the history
# Objective

- Fixes #14697

## Solution

This PR modifies the existing `all_tuples!` macro to optionally accept a
`#[doc(fake_variadic)]` attribute in its input. If the attribute is
present, each invocation of the impl macro gets the correct attributes
(i.e. the first impl receives `#[doc(fake_variadic)]` while the other
impls are hidden using `#[doc(hidden)]`.
Impls for the empty tuple (unit type) are left untouched (that's what
the [standard
library](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#impl-PartialEq-for-())
and
[serde](https://docs.rs/serde/latest/serde/trait.Serialize.html#impl-Serialize-for-())
do).

To work around rust-lang/cargo#8811 and to get
impls on re-exports to correctly show up as variadic, `--cfg docsrs_dep`
is passed when building the docs for the toplevel `bevy` crate.

`#[doc(fake_variadic)]` only works on tuples and fn pointers, so impls
for structs like `AnyOf<(T1, T2, ..., Tn)>` are unchanged.

## Testing

I built the docs locally using `RUSTDOCFLAGS='--cfg docsrs'
RUSTFLAGS='--cfg docsrs_dep' cargo +nightly doc --no-deps --workspace`
and checked the documentation page of a trait both in its original crate
and the re-exported version in `bevy`.
The description should correctly mention for how many tuple items the
trait is implemented.

I added `rustc-args` for docs.rs to the `bevy` crate, I hope there
aren't any other notable crates that re-export `#[doc(fake_variadic)]`
traits.

---

## Showcase

`bevy_ecs::query::QueryData`:
<img width="1015" alt="Screenshot 2024-08-12 at 16 41 28"
src="https://github.com/user-attachments/assets/d40136ed-6731-475f-91a0-9df255cd24e3">

`bevy::ecs::query::QueryData` (re-export):
<img width="1005" alt="Screenshot 2024-08-12 at 16 42 57"
src="https://github.com/user-attachments/assets/71d44cf0-0ab0-48b0-9a51-5ce332594e12">

## Original Description

<details>

Resolves #14697

Submitting as a draft for now, very WIP.

Unfortunately, the docs don't show the variadics nicely when looking at
reexported items.
For example:

`bevy_ecs::bundle::Bundle` correctly shows the variadic impl:

![image](https://github.com/user-attachments/assets/90bf8af1-1d1f-4714-9143-cdd3d0199998)

while `bevy::ecs::bundle::Bundle` (the reexport) shows all the impls
(not good):

![image](https://github.com/user-attachments/assets/439c428e-f712-465b-bec2-481f7bf5870b)

Built using `RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc --workspace
--no-deps` (`--no-deps` because of wgpu-core).

Maybe I missed something or this is a limitation in the *totally not
private* `#[doc(fake_variadic)]` thingy. In any case I desperately need
some sleep now :))

</details>
  • Loading branch information
bash authored Aug 12, 2024
1 parent 6ab8767 commit aab1f8e
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 57 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ref_as_ptr = "warn"
unsafe_op_in_unsafe_fn = "warn"
missing_docs = "warn"
unsafe_code = "deny"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] }

[lints]
workspace = true
Expand Down Expand Up @@ -3363,6 +3364,11 @@ lto = "fat"
panic = "abort"

[package.metadata.docs.rs]
# This cfg is needed so that #[doc(fake_variadic)] is correctly propagated for
# impls for re-exported traits. See https://github.com/rust-lang/cargo/issues/8811
# for details on why this is needed. Since dependencies don't expect to be built
# with `--cfg docsrs` (and thus fail to compile) we use a different cfg.
rustc-args = ["--cfg docsrs_dep"]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
Expand Down
11 changes: 9 additions & 2 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ impl<C: Component> DynamicBundle for C {
}

macro_rules! tuple_impl {
($($name: ident),*) => {
($(#[$meta:meta])* $($name: ident),*) => {
$(#[$meta])*
// SAFETY:
// - `Bundle::component_ids` calls `ids` for each component type in the
// bundle, in the exact order that `DynamicBundle::get_components` is called.
Expand Down Expand Up @@ -270,7 +271,13 @@ macro_rules! tuple_impl {
}
}

all_tuples!(tuple_impl, 0, 15, B);
all_tuples!(
#[doc(fake_variadic)]
tuple_impl,
0,
15,
B
);

/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].
///
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// FIXME(11590): remove this once the lint is fixed
#![allow(unsafe_op_in_unsafe_fn)]
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
#![allow(internal_features)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![allow(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
12 changes: 10 additions & 2 deletions crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1793,10 +1793,11 @@ unsafe impl<T: Component> ReadOnlyQueryData for Has<T> {}
pub struct AnyOf<T>(PhantomData<T>);

macro_rules! impl_tuple_query_data {
($(($name: ident, $state: ident)),*) => {
($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {

#[allow(non_snake_case)]
#[allow(clippy::unused_unit)]
$(#[$meta])*
// SAFETY: defers to soundness `$name: WorldQuery` impl
unsafe impl<$($name: QueryData),*> QueryData for ($($name,)*) {
type ReadOnly = ($($name::ReadOnly,)*);
Expand Down Expand Up @@ -1938,7 +1939,14 @@ macro_rules! impl_anytuple_fetch {
};
}

all_tuples!(impl_tuple_query_data, 0, 15, F, S);
all_tuples!(
#[doc(fake_variadic)]
impl_tuple_query_data,
0,
15,
F,
S
);
all_tuples!(impl_anytuple_fetch, 0, 15, F, S);

/// [`WorldQuery`] used to nullify queries by turning `Query<D>` into `Query<NopWorldQuery<D>>`
Expand Down
29 changes: 24 additions & 5 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,11 @@ macro_rules! impl_or_query_filter {
}

macro_rules! impl_tuple_query_filter {
($($name: ident),*) => {
($(#[$meta:meta])* $($name: ident),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
#[allow(clippy::unused_unit)]

$(#[$meta])*
impl<$($name: QueryFilter),*> QueryFilter for ($($name,)*) {
const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*;

Expand All @@ -516,7 +516,13 @@ macro_rules! impl_tuple_query_filter {
};
}

all_tuples!(impl_tuple_query_filter, 0, 15, F);
all_tuples!(
#[doc(fake_variadic)]
impl_tuple_query_filter,
0,
15,
F
);
all_tuples!(impl_or_query_filter, 0, 15, F, S);

/// A filter on a component that only retains results the first time after they have been added.
Expand Down Expand Up @@ -958,11 +964,24 @@ impl<T: Component> ArchetypeFilter for With<T> {}
impl<T: Component> ArchetypeFilter for Without<T> {}

macro_rules! impl_archetype_filter_tuple {
($($filter: ident),*) => {
($(#[$meta:meta])* $($filter: ident),*) => {
$(#[$meta])*
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {}
};
}

macro_rules! impl_archetype_or_filter_tuple {
($($filter: ident),*) => {
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {}
};
}

all_tuples!(impl_archetype_filter_tuple, 0, 15, F);
all_tuples!(
#[doc(fake_variadic)]
impl_archetype_filter_tuple,
0,
15,
F
);

all_tuples!(impl_archetype_or_filter_tuple, 0, 15, F);
12 changes: 10 additions & 2 deletions crates/bevy_ecs/src/query/world_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ pub unsafe trait WorldQuery {
}

macro_rules! impl_tuple_world_query {
($(($name: ident, $state: ident)),*) => {
($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {

#[allow(non_snake_case)]
#[allow(clippy::unused_unit)]
$(#[$meta])*
/// SAFETY:
/// `fetch` accesses are the conjunction of the subqueries' accesses
/// This is sound because `update_component_access` adds accesses according to the implementations of all the subqueries.
Expand Down Expand Up @@ -229,4 +230,11 @@ macro_rules! impl_tuple_world_query {
};
}

all_tuples!(impl_tuple_world_query, 0, 15, F, S);
all_tuples!(
#[doc(fake_variadic)]
impl_tuple_world_query,
0,
15,
F,
S
);
23 changes: 19 additions & 4 deletions crates/bevy_ecs/src/schedule/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,8 @@ impl IntoSystemConfigs<()> for SystemConfigs {
pub struct SystemConfigTupleMarker;

macro_rules! impl_system_collection {
($(($param: ident, $sys: ident)),*) => {
($(#[$meta:meta])* $(($param: ident, $sys: ident)),*) => {
$(#[$meta])*
impl<$($param, $sys),*> IntoSystemConfigs<(SystemConfigTupleMarker, $($param,)*)> for ($($sys,)*)
where
$($sys: IntoSystemConfigs<$param>),*
Expand All @@ -539,7 +540,14 @@ macro_rules! impl_system_collection {
}
}

all_tuples!(impl_system_collection, 1, 20, P, S);
all_tuples!(
#[doc(fake_variadic)]
impl_system_collection,
1,
20,
P,
S
);

/// A [`SystemSet`] with scheduling metadata.
pub type SystemSetConfig = NodeConfig<InternedSystemSet>;
Expand Down Expand Up @@ -740,7 +748,8 @@ impl IntoSystemSetConfigs for SystemSetConfig {
}

macro_rules! impl_system_set_collection {
($($set: ident),*) => {
($(#[$meta:meta])* $($set: ident),*) => {
$(#[$meta])*
impl<$($set: IntoSystemSetConfigs),*> IntoSystemSetConfigs for ($($set,)*)
{
#[allow(non_snake_case)]
Expand All @@ -756,4 +765,10 @@ macro_rules! impl_system_set_collection {
}
}

all_tuples!(impl_system_set_collection, 1, 20, S);
all_tuples!(
#[doc(fake_variadic)]
impl_system_set_collection,
1,
20,
S
);
11 changes: 9 additions & 2 deletions crates/bevy_ecs/src/system/exclusive_system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@ impl<S: ?Sized> ExclusiveSystemParam for PhantomData<S> {
}

macro_rules! impl_exclusive_system_param_tuple {
($($param: ident),*) => {
($(#[$meta:meta])* $($param: ident),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
$(#[$meta])*
impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
type State = ($($param::State,)*);
type Item<'s> = ($($param::Item<'s>,)*);
Expand All @@ -113,7 +114,13 @@ macro_rules! impl_exclusive_system_param_tuple {
};
}

all_tuples!(impl_exclusive_system_param_tuple, 0, 16, P);
all_tuples!(
#[doc(fake_variadic)]
impl_exclusive_system_param_tuple,
0,
16,
P
);

#[cfg(test)]
mod tests {
Expand Down
14 changes: 11 additions & 3 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ fn assert_component_access_compatibility(
/// # let _event = event;
/// }
/// set.p1().send(MyEvent::new());
///
///
/// let entities = set.p2().entities();
/// // ...
/// # let _entities = entities;
Expand Down Expand Up @@ -1422,13 +1422,15 @@ unsafe impl SystemParam for SystemChangeTick {
}

macro_rules! impl_system_param_tuple {
($($param: ident),*) => {
($(#[$meta:meta])* $($param: ident),*) => {
$(#[$meta])*
// SAFETY: tuple consists only of ReadOnlySystemParams
unsafe impl<$($param: ReadOnlySystemParam),*> ReadOnlySystemParam for ($($param,)*) {}

// SAFETY: implementors of each `SystemParam` in the tuple have validated their impls
#[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy
#[allow(non_snake_case)]
$(#[$meta])*
unsafe impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
type State = ($($param::State,)*);
type Item<'w, 's> = ($($param::Item::<'w, 's>,)*);
Expand Down Expand Up @@ -1471,7 +1473,13 @@ macro_rules! impl_system_param_tuple {
};
}

all_tuples!(impl_system_param_tuple, 0, 16, P);
all_tuples!(
#[doc(fake_variadic)]
impl_system_param_tuple,
0,
16,
P
);

/// Contains type aliases for built-in [`SystemParam`]s with `'static` lifetimes.
/// This makes it more convenient to refer to these types in contexts where
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
#![allow(internal_features)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
17 changes: 12 additions & 5 deletions crates/bevy_reflect/src/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J,
impl_reflect_tuple! {0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L}

macro_rules! impl_type_path_tuple {
() => {
($(#[$meta:meta])*) => {
impl TypePath for () {
fn type_path() -> &'static str {
"()"
Expand All @@ -714,7 +714,8 @@ macro_rules! impl_type_path_tuple {
}
};

($param:ident) => {
($(#[$meta:meta])* $param:ident) => {
$(#[$meta])*
impl <$param: TypePath> TypePath for ($param,) {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
Expand All @@ -732,8 +733,8 @@ macro_rules! impl_type_path_tuple {
}
};

($last:ident $(,$param:ident)*) => {

($(#[$meta:meta])* $last:ident $(,$param:ident)*) => {
$(#[$meta])*
impl <$($param: TypePath,)* $last: TypePath> TypePath for ($($param,)* $last) {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
Expand All @@ -752,7 +753,13 @@ macro_rules! impl_type_path_tuple {
};
}

all_tuples!(impl_type_path_tuple, 0, 12, P);
all_tuples!(
#[doc(fake_variadic)]
impl_type_path_tuple,
0,
12,
P
);

#[cfg(feature = "functions")]
const _: () = {
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![allow(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
#![allow(internal_features)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
13 changes: 11 additions & 2 deletions crates/bevy_render/src/render_phase/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ pub enum RenderCommandResult {
}

macro_rules! render_command_tuple_impl {
($(($name: ident, $view: ident, $entity: ident)),*) => {
($(#[$meta:meta])* $(($name: ident, $view: ident, $entity: ident)),*) => {
$(#[$meta])*
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
type Param = ($($name::Param,)*);
type ViewQuery = ($($name::ViewQuery,)*);
Expand Down Expand Up @@ -268,7 +269,15 @@ macro_rules! render_command_tuple_impl {
};
}

all_tuples!(render_command_tuple_impl, 0, 15, C, V, E);
all_tuples!(
#[doc(fake_variadic)]
render_command_tuple_impl,
0,
15,
C,
V,
E
);

/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
///
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
//! - The [`in_state<S>`](crate::condition::in_state) and [`state_changed<S>`](crate::condition::state_changed) run conditions - which are used
//! to determine whether a system should run based on the current state.
// `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
#![allow(internal_features)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]

#[cfg(feature = "bevy_app")]
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods
pub mod app;
Expand Down
Loading

0 comments on commit aab1f8e

Please sign in to comment.