From 0036f819fa85ee4c20f2e8a3d11bce1483e0d962 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 6 Mar 2025 11:33:41 +1100 Subject: [PATCH 1/5] Initial Commit --- crates/bevy_ecs/macros/src/component.rs | 24 ++++++++++++++++++++++++ crates/bevy_ecs/src/component.rs | 3 +++ 2 files changed, 27 insertions(+) diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index f88ae4349cc0b..190cba04b8158 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -207,6 +207,28 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let required_component_impls = attrs.requires.as_ref().map(|r| { + let impls = r + .iter() + .map(|r| { + let path = &r.path; + let insertion_info = match &r.func { + Some(RequireFunc::Closure(closure)) => format!("`{}`", closure.body.to_token_stream()), + Some(RequireFunc::Path(path)) => format!("[`{}`].", path.to_token_stream()), + None => "the [default](Default::default) value.".to_string(), + }; + quote! { + /// If not already present, the required component will be inserted using + #[doc = #insertion_info] + impl #impl_generics #bevy_ecs_path::component::Require<#path> for #struct_name #type_generics #where_clause {} + } + }); + + quote! { + #(#impls)* + } + }); + let required_component_docs = attrs.requires.map(|r| { let paths = r .iter() @@ -270,6 +292,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { #relationship #relationship_target + + #required_component_impls }) } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 7a67571d89056..c58b2e36575ee 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -455,6 +455,9 @@ pub trait Component: Send + Sync + 'static { fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {} } +/// Indicates this [`Component`] requires another [`Component`] `C`. +pub trait Require: Component {} + mod private { pub trait Seal {} } From 0cd1cdabab80dc562f67752032983a46f2ca9658 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 6 Mar 2025 12:06:27 +1100 Subject: [PATCH 2/5] Make `Require` an `unsafe` trait Discourages users from implementing it themselves, since it is purely for documentation purposes. Co-Authored-By: JoshValjosh <48692273+jnhyatt@users.noreply.github.com> --- crates/bevy_ecs/macros/src/component.rs | 2 +- crates/bevy_ecs/src/component.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 190cba04b8158..aaa6de9001140 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -220,7 +220,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { quote! { /// If not already present, the required component will be inserted using #[doc = #insertion_info] - impl #impl_generics #bevy_ecs_path::component::Require<#path> for #struct_name #type_generics #where_clause {} + unsafe impl #impl_generics #bevy_ecs_path::component::Require<#path> for #struct_name #type_generics #where_clause {} } }); diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index c58b2e36575ee..73184c5e26db8 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -456,7 +456,13 @@ pub trait Component: Send + Sync + 'static { } /// Indicates this [`Component`] requires another [`Component`] `C`. -pub trait Require: Component {} +/// This trait is similar to [`Eq`] in the sense that it is up to the implementer to ensure `C` is +/// appropriately registered as a required component. +/// +/// # Safety +/// +/// Implementing this trait must be done in tandem with updating the implementation of [`Component::register_required_components`]. +pub unsafe trait Require: Component {} mod private { pub trait Seal {} From 16f0a53b2a0c35d2eaf1301f6967bb6a45b70529 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 7 Mar 2025 10:39:59 +1100 Subject: [PATCH 3/5] Refactor Co-Authored-By: Alice Cecile --- crates/bevy_ecs/macros/src/component.rs | 2 +- crates/bevy_ecs/src/component.rs | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index aaa6de9001140..8917318eab072 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -220,7 +220,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { quote! { /// If not already present, the required component will be inserted using #[doc = #insertion_info] - unsafe impl #impl_generics #bevy_ecs_path::component::Require<#path> for #struct_name #type_generics #where_clause {} + unsafe impl #impl_generics #bevy_ecs_path::component::document_required_components::Require<#path> for #struct_name #type_generics #where_clause {} } }); diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 73184c5e26db8..7d48c7fb0a24e 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -455,14 +455,22 @@ pub trait Component: Send + Sync + 'static { fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {} } -/// Indicates this [`Component`] requires another [`Component`] `C`. -/// This trait is similar to [`Eq`] in the sense that it is up to the implementer to ensure `C` is -/// appropriately registered as a required component. -/// -/// # Safety -/// -/// Implementing this trait must be done in tandem with updating the implementation of [`Component::register_required_components`]. -pub unsafe trait Require: Component {} +// doc(hidden) module makes it clear that usage of this trait outside of `bevy_ecs` is unsupported. +#[doc(hidden)] +pub mod document_required_components { + use super::Component; + + /// Indicates this [`Component`] requires another [`Component`] `C`. + /// + /// **This trait does not register required components on its own** + /// + /// This trait is similar to [`Eq`] in the sense that it is up to the implementer to ensure `C` is + /// appropriately registered as a required component. + /// + /// It is the implementor's responsibility to ensure the implementation of [`Component::register_required_components`] + /// matches any and all implementations of [`Require`] on this type. + pub trait Require: Component {} +} mod private { pub trait Seal {} From d2c0e4c806271385d672f93211d6389294446144 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 7 Mar 2025 10:41:59 +1100 Subject: [PATCH 4/5] Remove `unsafe` --- crates/bevy_ecs/macros/src/component.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 8917318eab072..5d51c615c7f48 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -220,7 +220,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { quote! { /// If not already present, the required component will be inserted using #[doc = #insertion_info] - unsafe impl #impl_generics #bevy_ecs_path::component::document_required_components::Require<#path> for #struct_name #type_generics #where_clause {} + impl #impl_generics #bevy_ecs_path::component::document_required_components::Require<#path> for #struct_name #type_generics #where_clause {} } }); From 659eedb1b25f325339d7231f3aa730d46f3d32a5 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 7 Mar 2025 14:28:40 +1100 Subject: [PATCH 5/5] Update documentation Co-Authored-By: Alice Cecile --- crates/bevy_ecs/src/component.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 7d48c7fb0a24e..6470925ab94c3 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -462,13 +462,14 @@ pub mod document_required_components { /// Indicates this [`Component`] requires another [`Component`] `C`. /// - /// **This trait does not register required components on its own** + /// **This trait does not register required components on its own.** /// - /// This trait is similar to [`Eq`] in the sense that it is up to the implementer to ensure `C` is - /// appropriately registered as a required component. + /// This trait is similar to [`Eq`] in the sense that it is up to the implementer to ensure `C` + /// is appropriately registered as a required component. /// - /// It is the implementor's responsibility to ensure the implementation of [`Component::register_required_components`] - /// matches any and all implementations of [`Require`] on this type. + /// You should never manually implement this trait, but it is the implementor's responsibility + /// to ensure the implementation of [`Component::register_required_components`] matches any and + /// all implementations of [`Require`] on this type. pub trait Require: Component {} }