Use #[doc(fake_variadic)] to improve docs readability (#14703)
# 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 https://github.com/rust-lang/cargo/issues/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:  while `bevy::ecs::bundle::Bundle` (the reexport) shows all the impls (not good):  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>
This commit is contained in:
parent
6ab8767d3b
commit
aab1f8e435
@ -49,6 +49,7 @@ ref_as_ptr = "warn"
|
|||||||
unsafe_op_in_unsafe_fn = "warn"
|
unsafe_op_in_unsafe_fn = "warn"
|
||||||
missing_docs = "warn"
|
missing_docs = "warn"
|
||||||
unsafe_code = "deny"
|
unsafe_code = "deny"
|
||||||
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
@ -3363,6 +3364,11 @@ lto = "fat"
|
|||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[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"]
|
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
|
||||||
all-features = true
|
all-features = true
|
||||||
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
|
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
|
||||||
|
|||||||
@ -225,7 +225,8 @@ impl<C: Component> DynamicBundle for C {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! tuple_impl {
|
macro_rules! tuple_impl {
|
||||||
($($name: ident),*) => {
|
($(#[$meta:meta])* $($name: ident),*) => {
|
||||||
|
$(#[$meta])*
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - `Bundle::component_ids` calls `ids` for each component type in the
|
// - `Bundle::component_ids` calls `ids` for each component type in the
|
||||||
// bundle, in the exact order that `DynamicBundle::get_components` is called.
|
// bundle, in the exact order that `DynamicBundle::get_components` is called.
|
||||||
@ -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`].
|
/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`].
|
||||||
///
|
///
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
// FIXME(11590): remove this once the lint is fixed
|
// FIXME(11590): remove this once the lint is fixed
|
||||||
#![allow(unsafe_op_in_unsafe_fn)]
|
#![allow(unsafe_op_in_unsafe_fn)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![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)]
|
#![allow(unsafe_code)]
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||||
|
|||||||
@ -1793,10 +1793,11 @@ unsafe impl<T: Component> ReadOnlyQueryData for Has<T> {}
|
|||||||
pub struct AnyOf<T>(PhantomData<T>);
|
pub struct AnyOf<T>(PhantomData<T>);
|
||||||
|
|
||||||
macro_rules! impl_tuple_query_data {
|
macro_rules! impl_tuple_query_data {
|
||||||
($(($name: ident, $state: ident)),*) => {
|
($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::unused_unit)]
|
#[allow(clippy::unused_unit)]
|
||||||
|
$(#[$meta])*
|
||||||
// SAFETY: defers to soundness `$name: WorldQuery` impl
|
// SAFETY: defers to soundness `$name: WorldQuery` impl
|
||||||
unsafe impl<$($name: QueryData),*> QueryData for ($($name,)*) {
|
unsafe impl<$($name: QueryData),*> QueryData for ($($name,)*) {
|
||||||
type ReadOnly = ($($name::ReadOnly,)*);
|
type ReadOnly = ($($name::ReadOnly,)*);
|
||||||
@ -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);
|
all_tuples!(impl_anytuple_fetch, 0, 15, F, S);
|
||||||
|
|
||||||
/// [`WorldQuery`] used to nullify queries by turning `Query<D>` into `Query<NopWorldQuery<D>>`
|
/// [`WorldQuery`] used to nullify queries by turning `Query<D>` into `Query<NopWorldQuery<D>>`
|
||||||
|
|||||||
@ -493,11 +493,11 @@ macro_rules! impl_or_query_filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_tuple_query_filter {
|
macro_rules! impl_tuple_query_filter {
|
||||||
($($name: ident),*) => {
|
($(#[$meta:meta])* $($name: ident),*) => {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::unused_unit)]
|
#[allow(clippy::unused_unit)]
|
||||||
|
$(#[$meta])*
|
||||||
impl<$($name: QueryFilter),*> QueryFilter for ($($name,)*) {
|
impl<$($name: QueryFilter),*> QueryFilter for ($($name,)*) {
|
||||||
const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*;
|
const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*;
|
||||||
|
|
||||||
@ -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);
|
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.
|
/// A filter on a component that only retains results the first time after they have been added.
|
||||||
@ -958,11 +964,24 @@ impl<T: Component> ArchetypeFilter for With<T> {}
|
|||||||
impl<T: Component> ArchetypeFilter for Without<T> {}
|
impl<T: Component> ArchetypeFilter for Without<T> {}
|
||||||
|
|
||||||
macro_rules! impl_archetype_filter_tuple {
|
macro_rules! impl_archetype_filter_tuple {
|
||||||
($($filter: ident),*) => {
|
($(#[$meta:meta])* $($filter: ident),*) => {
|
||||||
|
$(#[$meta])*
|
||||||
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {}
|
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_archetype_or_filter_tuple {
|
||||||
|
($($filter: ident),*) => {
|
||||||
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {}
|
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);
|
||||||
|
|||||||
@ -144,10 +144,11 @@ pub unsafe trait WorldQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_tuple_world_query {
|
macro_rules! impl_tuple_world_query {
|
||||||
($(($name: ident, $state: ident)),*) => {
|
($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(clippy::unused_unit)]
|
#[allow(clippy::unused_unit)]
|
||||||
|
$(#[$meta])*
|
||||||
/// SAFETY:
|
/// SAFETY:
|
||||||
/// `fetch` accesses are the conjunction of the subqueries' accesses
|
/// `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.
|
/// This is sound because `update_component_access` adds accesses according to the implementations of all the subqueries.
|
||||||
@ -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
|
||||||
|
);
|
||||||
|
|||||||
@ -521,7 +521,8 @@ impl IntoSystemConfigs<()> for SystemConfigs {
|
|||||||
pub struct SystemConfigTupleMarker;
|
pub struct SystemConfigTupleMarker;
|
||||||
|
|
||||||
macro_rules! impl_system_collection {
|
macro_rules! impl_system_collection {
|
||||||
($(($param: ident, $sys: ident)),*) => {
|
($(#[$meta:meta])* $(($param: ident, $sys: ident)),*) => {
|
||||||
|
$(#[$meta])*
|
||||||
impl<$($param, $sys),*> IntoSystemConfigs<(SystemConfigTupleMarker, $($param,)*)> for ($($sys,)*)
|
impl<$($param, $sys),*> IntoSystemConfigs<(SystemConfigTupleMarker, $($param,)*)> for ($($sys,)*)
|
||||||
where
|
where
|
||||||
$($sys: IntoSystemConfigs<$param>),*
|
$($sys: IntoSystemConfigs<$param>),*
|
||||||
@ -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.
|
/// A [`SystemSet`] with scheduling metadata.
|
||||||
pub type SystemSetConfig = NodeConfig<InternedSystemSet>;
|
pub type SystemSetConfig = NodeConfig<InternedSystemSet>;
|
||||||
@ -740,7 +748,8 @@ impl IntoSystemSetConfigs for SystemSetConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_system_set_collection {
|
macro_rules! impl_system_set_collection {
|
||||||
($($set: ident),*) => {
|
($(#[$meta:meta])* $($set: ident),*) => {
|
||||||
|
$(#[$meta])*
|
||||||
impl<$($set: IntoSystemSetConfigs),*> IntoSystemSetConfigs for ($($set,)*)
|
impl<$($set: IntoSystemSetConfigs),*> IntoSystemSetConfigs for ($($set,)*)
|
||||||
{
|
{
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
@ -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
|
||||||
|
);
|
||||||
|
|||||||
@ -87,9 +87,10 @@ impl<S: ?Sized> ExclusiveSystemParam for PhantomData<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_exclusive_system_param_tuple {
|
macro_rules! impl_exclusive_system_param_tuple {
|
||||||
($($param: ident),*) => {
|
($(#[$meta:meta])* $($param: ident),*) => {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
$(#[$meta])*
|
||||||
impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
|
impl<$($param: ExclusiveSystemParam),*> ExclusiveSystemParam for ($($param,)*) {
|
||||||
type State = ($($param::State,)*);
|
type State = ($($param::State,)*);
|
||||||
type Item<'s> = ($($param::Item<'s>,)*);
|
type Item<'s> = ($($param::Item<'s>,)*);
|
||||||
@ -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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@ -1422,13 +1422,15 @@ unsafe impl SystemParam for SystemChangeTick {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_system_param_tuple {
|
macro_rules! impl_system_param_tuple {
|
||||||
($($param: ident),*) => {
|
($(#[$meta:meta])* $($param: ident),*) => {
|
||||||
|
$(#[$meta])*
|
||||||
// SAFETY: tuple consists only of ReadOnlySystemParams
|
// SAFETY: tuple consists only of ReadOnlySystemParams
|
||||||
unsafe impl<$($param: ReadOnlySystemParam),*> ReadOnlySystemParam for ($($param,)*) {}
|
unsafe impl<$($param: ReadOnlySystemParam),*> ReadOnlySystemParam for ($($param,)*) {}
|
||||||
|
|
||||||
// SAFETY: implementors of each `SystemParam` in the tuple have validated their impls
|
// SAFETY: implementors of each `SystemParam` in the tuple have validated their impls
|
||||||
#[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy
|
#[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
$(#[$meta])*
|
||||||
unsafe impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
|
unsafe impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
|
||||||
type State = ($($param::State,)*);
|
type State = ($($param::State,)*);
|
||||||
type Item<'w, 's> = ($($param::Item::<'w, 's>,)*);
|
type Item<'w, 's> = ($($param::Item::<'w, 's>,)*);
|
||||||
@ -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.
|
/// Contains type aliases for built-in [`SystemParam`]s with `'static` lifetimes.
|
||||||
/// This makes it more convenient to refer to these types in contexts where
|
/// This makes it more convenient to refer to these types in contexts where
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// FIXME(3492): remove once docs are ready
|
// FIXME(3492): remove once docs are ready
|
||||||
#![allow(missing_docs)]
|
#![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(
|
#![doc(
|
||||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||||
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
||||||
|
|||||||
@ -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}
|
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 {
|
macro_rules! impl_type_path_tuple {
|
||||||
() => {
|
($(#[$meta:meta])*) => {
|
||||||
impl TypePath for () {
|
impl TypePath for () {
|
||||||
fn type_path() -> &'static str {
|
fn type_path() -> &'static str {
|
||||||
"()"
|
"()"
|
||||||
@ -714,7 +714,8 @@ macro_rules! impl_type_path_tuple {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
($param:ident) => {
|
($(#[$meta:meta])* $param:ident) => {
|
||||||
|
$(#[$meta])*
|
||||||
impl <$param: TypePath> TypePath for ($param,) {
|
impl <$param: TypePath> TypePath for ($param,) {
|
||||||
fn type_path() -> &'static str {
|
fn type_path() -> &'static str {
|
||||||
static CELL: GenericTypePathCell = GenericTypePathCell::new();
|
static CELL: GenericTypePathCell = GenericTypePathCell::new();
|
||||||
@ -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) {
|
impl <$($param: TypePath,)* $last: TypePath> TypePath for ($($param,)* $last) {
|
||||||
fn type_path() -> &'static str {
|
fn type_path() -> &'static str {
|
||||||
static CELL: GenericTypePathCell = GenericTypePathCell::new();
|
static CELL: GenericTypePathCell = GenericTypePathCell::new();
|
||||||
@ -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")]
|
#[cfg(feature = "functions")]
|
||||||
const _: () = {
|
const _: () = {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
// FIXME(3492): remove once docs are ready
|
// FIXME(3492): remove once docs are ready
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
#![allow(unsafe_code)]
|
#![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(
|
#![doc(
|
||||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||||
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
||||||
|
|||||||
@ -228,7 +228,8 @@ pub enum RenderCommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! render_command_tuple_impl {
|
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,)*) {
|
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
|
||||||
type Param = ($($name::Param,)*);
|
type Param = ($($name::Param,)*);
|
||||||
type ViewQuery = ($($name::ViewQuery,)*);
|
type ViewQuery = ($($name::ViewQuery,)*);
|
||||||
@ -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.
|
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -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
|
//! - 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.
|
//! 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")]
|
#[cfg(feature = "bevy_app")]
|
||||||
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods
|
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|||||||
@ -229,10 +229,12 @@ impl<S: InnerStateSet> StateSet for S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_state_set_sealed_tuples {
|
macro_rules! impl_state_set_sealed_tuples {
|
||||||
($(($param: ident, $val: ident, $evt: ident)), *) => {
|
($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {
|
||||||
impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}
|
$(#[$meta])*
|
||||||
|
impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}
|
||||||
|
|
||||||
impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {
|
$(#[$meta])*
|
||||||
|
impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {
|
||||||
|
|
||||||
const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;
|
const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;
|
||||||
|
|
||||||
@ -338,4 +340,12 @@ macro_rules! impl_state_set_sealed_tuples {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
all_tuples!(impl_state_set_sealed_tuples, 1, 15, S, s, ereader);
|
all_tuples!(
|
||||||
|
#[doc(fake_variadic)]
|
||||||
|
impl_state_set_sealed_tuples,
|
||||||
|
1,
|
||||||
|
15,
|
||||||
|
S,
|
||||||
|
s,
|
||||||
|
ereader
|
||||||
|
);
|
||||||
|
|||||||
@ -3,14 +3,17 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
parse_macro_input,
|
parse_macro_input,
|
||||||
|
spanned::Spanned as _,
|
||||||
token::Comma,
|
token::Comma,
|
||||||
Ident, LitInt, Result,
|
Attribute, Error, Ident, LitInt, LitStr, Result,
|
||||||
};
|
};
|
||||||
struct AllTuples {
|
struct AllTuples {
|
||||||
|
fake_variadic: bool,
|
||||||
macro_ident: Ident,
|
macro_ident: Ident,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
@ -19,6 +22,7 @@ struct AllTuples {
|
|||||||
|
|
||||||
impl Parse for AllTuples {
|
impl Parse for AllTuples {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let fake_variadic = input.call(parse_fake_variadic_attr)?;
|
||||||
let macro_ident = input.parse::<Ident>()?;
|
let macro_ident = input.parse::<Ident>()?;
|
||||||
input.parse::<Comma>()?;
|
input.parse::<Comma>()?;
|
||||||
let start = input.parse::<LitInt>()?.base10_parse()?;
|
let start = input.parse::<LitInt>()?.base10_parse()?;
|
||||||
@ -30,7 +34,15 @@ impl Parse for AllTuples {
|
|||||||
idents.push(input.parse::<Ident>()?);
|
idents.push(input.parse::<Ident>()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if start > 1 && fake_variadic {
|
||||||
|
return Err(Error::new(
|
||||||
|
input.span(),
|
||||||
|
"#[doc(fake_variadic)] only works when the tuple with length one is included",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(AllTuples {
|
Ok(AllTuples {
|
||||||
|
fake_variadic,
|
||||||
macro_ident,
|
macro_ident,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
@ -105,7 +117,42 @@ impl Parse for AllTuples {
|
|||||||
/// // impl_append!((P0, p0), (P1, p1), (P2, p2));
|
/// // impl_append!((P0, p0), (P1, p1), (P2, p2));
|
||||||
/// // ..
|
/// // ..
|
||||||
/// // impl_append!((P0, p0) .. (P14, p14));
|
/// // impl_append!((P0, p0) .. (P14, p14));
|
||||||
/// ````
|
/// ```
|
||||||
|
///
|
||||||
|
/// **`#[doc(fake_variadic)]`**
|
||||||
|
///
|
||||||
|
/// To improve the readability of your docs when implementing a trait for
|
||||||
|
/// tuples or fn pointers of varying length you can use the rustdoc-internal `fake_variadic` marker.
|
||||||
|
/// All your impls are collapsed and shown as a single `impl Trait for (F₁, F₂, …, Fₙ)`.
|
||||||
|
///
|
||||||
|
/// The `all_tuples!` macro does most of the work for you, the only change to your implementation macro
|
||||||
|
/// is that you have to accept attributes using `$(#[$meta:meta])*`.
|
||||||
|
///
|
||||||
|
/// Since this feature requires a nightly compiler, it's only enabled on docs.rs by default.
|
||||||
|
/// Add the following to your lib.rs if not already present:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// // `rustdoc_internals` is needed for `#[doc(fake_variadics)]`
|
||||||
|
/// #![allow(internal_features)]
|
||||||
|
/// #![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use bevy_utils_proc_macros::all_tuples;
|
||||||
|
///
|
||||||
|
/// trait Variadic {}
|
||||||
|
///
|
||||||
|
/// impl Variadic for () {}
|
||||||
|
///
|
||||||
|
/// macro_rules! impl_variadic {
|
||||||
|
/// ($(#[$meta:meta])* $(($P:ident, $p:ident)),*) => {
|
||||||
|
/// $(#[$meta])*
|
||||||
|
/// impl<$($P),*> Variadic for ($($P,)*) {}
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// all_tuples!(#[doc(fake_variadic)] impl_variadic, 1, 15, P, p);
|
||||||
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn all_tuples(input: TokenStream) -> TokenStream {
|
pub fn all_tuples(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as AllTuples);
|
let input = parse_macro_input!(input as AllTuples);
|
||||||
@ -116,22 +163,19 @@ pub fn all_tuples(input: TokenStream) -> TokenStream {
|
|||||||
.idents
|
.idents
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ident| format_ident!("{}{}", ident, i));
|
.map(|ident| format_ident!("{}{}", ident, i));
|
||||||
if input.idents.len() < 2 {
|
ident_tuples.push(to_ident_tuple(idents, input.idents.len()));
|
||||||
ident_tuples.push(quote! {
|
|
||||||
#(#idents)*
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ident_tuples.push(quote! {
|
|
||||||
(#(#idents),*)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let macro_ident = &input.macro_ident;
|
let macro_ident = &input.macro_ident;
|
||||||
let invocations = (input.start..=input.end).map(|i| {
|
let invocations = (input.start..=input.end).map(|i| {
|
||||||
let ident_tuples = &ident_tuples[..i];
|
let ident_tuples = choose_ident_tuples(&input, &ident_tuples, i);
|
||||||
|
let attrs = if input.fake_variadic {
|
||||||
|
fake_variadic_attrs(len, i)
|
||||||
|
} else {
|
||||||
|
TokenStream2::default()
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
#macro_ident!(#(#ident_tuples),*);
|
#macro_ident!(#attrs #ident_tuples);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
@ -151,17 +195,8 @@ pub fn all_tuples_with_size(input: TokenStream) -> TokenStream {
|
|||||||
.idents
|
.idents
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ident| format_ident!("{}{}", ident, i));
|
.map(|ident| format_ident!("{}{}", ident, i));
|
||||||
if input.idents.len() < 2 {
|
ident_tuples.push(to_ident_tuple(idents, input.idents.len()));
|
||||||
ident_tuples.push(quote! {
|
|
||||||
#(#idents)*
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ident_tuples.push(quote! {
|
|
||||||
(#(#idents),*)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let macro_ident = &input.macro_ident;
|
let macro_ident = &input.macro_ident;
|
||||||
let invocations = (input.start..=input.end).map(|i| {
|
let invocations = (input.start..=input.end).map(|i| {
|
||||||
let ident_tuples = &ident_tuples[..i];
|
let ident_tuples = &ident_tuples[..i];
|
||||||
@ -175,3 +210,72 @@ pub fn all_tuples_with_size(input: TokenStream) -> TokenStream {
|
|||||||
)*
|
)*
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the attribute `#[doc(fake_variadic)]`
|
||||||
|
fn parse_fake_variadic_attr(input: ParseStream) -> Result<bool> {
|
||||||
|
let attribute = match input.call(Attribute::parse_outer)? {
|
||||||
|
attributes if attributes.is_empty() => return Ok(false),
|
||||||
|
attributes if attributes.len() == 1 => attributes[0].clone(),
|
||||||
|
attributes => {
|
||||||
|
return Err(Error::new(
|
||||||
|
input.span(),
|
||||||
|
format!("Expected exactly one attribute, got {}", attributes.len()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if attribute.path().is_ident("doc") {
|
||||||
|
let nested = attribute.parse_args::<Ident>()?;
|
||||||
|
if nested == "fake_variadic" {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::new(
|
||||||
|
attribute.meta.span(),
|
||||||
|
"Unexpected attribute".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_ident_tuples(input: &AllTuples, ident_tuples: &[TokenStream2], i: usize) -> TokenStream2 {
|
||||||
|
// `rustdoc` uses the first ident to generate nice
|
||||||
|
// idents with subscript numbers e.g. (F₁, F₂, …, Fₙ).
|
||||||
|
// We don't want two numbers, so we use the
|
||||||
|
// original, unnumbered idents for this case.
|
||||||
|
if input.fake_variadic && i == 1 {
|
||||||
|
let ident_tuple = to_ident_tuple(input.idents.iter().cloned(), input.idents.len());
|
||||||
|
quote! { #ident_tuple }
|
||||||
|
} else {
|
||||||
|
let ident_tuples = &ident_tuples[..i];
|
||||||
|
quote! { #(#ident_tuples),* }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_ident_tuple(idents: impl Iterator<Item = Ident>, len: usize) -> TokenStream2 {
|
||||||
|
if len < 2 {
|
||||||
|
quote! { #(#idents)* }
|
||||||
|
} else {
|
||||||
|
quote! { (#(#idents),*) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fake_variadic_attrs(len: usize, i: usize) -> TokenStream2 {
|
||||||
|
let cfg = quote! { any(docsrs, docsrs_dep) };
|
||||||
|
match i {
|
||||||
|
// An empty tuple (i.e. the unit type) is still documented separately,
|
||||||
|
// so no `#[doc(hidden)]` here.
|
||||||
|
0 => TokenStream2::default(),
|
||||||
|
// The `#[doc(fake_variadic)]` attr has to be on the first impl block.
|
||||||
|
1 => {
|
||||||
|
let doc = LitStr::new(
|
||||||
|
&format!("This trait is implemented for tuples up to {len} items long."),
|
||||||
|
Span2::call_site(),
|
||||||
|
);
|
||||||
|
quote! {
|
||||||
|
#[cfg_attr(#cfg, doc(fake_variadic))]
|
||||||
|
#[cfg_attr(#cfg, doc = #doc)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! { #[cfg_attr(#cfg, doc(hidden))] },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user