diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index f88ae4349cc0b..5d51c615c7f48 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::document_required_components::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..6470925ab94c3 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -455,6 +455,24 @@ pub trait Component: Send + Sync + 'static { fn visit_entities_mut(_this: &mut Self, _f: impl FnMut(&mut Entity)) {} } +// 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. + /// + /// 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 {} +} + mod private { pub trait Seal {} }