Skip to content

Commit 88330ad

Browse files
committed
Add automatic reflection registration
1 parent ea2ecd4 commit 88330ad

File tree

9 files changed

+211
-41
lines changed

9 files changed

+211
-41
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

+39-25
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use quote::quote;
88
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
99
use syn::punctuated::Punctuated;
1010
use syn::spanned::Spanned;
11-
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant};
11+
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant};
1212

1313
pub(crate) enum ReflectDerive<'a> {
1414
Struct(ReflectStruct<'a>),
@@ -328,6 +328,7 @@ impl<'a> ReflectMeta<'a> {
328328
self.generics,
329329
where_clause_options,
330330
None,
331+
None::<std::iter::Empty<&Type>>,
331332
)
332333
}
333334

@@ -352,35 +353,16 @@ impl<'a> ReflectStruct<'a> {
352353
&self.serialization_denylist
353354
}
354355

355-
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
356-
///
357-
/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method
358-
pub fn get_type_registration(
359-
&self,
360-
where_clause_options: &WhereClauseOptions,
361-
) -> proc_macro2::TokenStream {
362-
let reflect_path = self.meta.bevy_reflect_path();
363-
364-
crate::registration::impl_get_type_registration(
365-
self.meta.type_name(),
366-
reflect_path,
367-
self.meta.traits().idents(),
368-
self.meta.generics(),
369-
where_clause_options,
370-
Some(&self.serialization_denylist),
371-
)
372-
}
373-
374356
/// Get a collection of types which are exposed to the reflection API
375357
pub fn active_types(&self) -> Vec<syn::Type> {
376358
self.active_fields()
377359
.map(|field| field.data.ty.clone())
378360
.collect()
379361
}
380362

381-
/// Get an iterator of fields which are exposed to the reflection API
363+
/// Get an iterator of fields which are exposed to the reflection API.
382364
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
383-
self.fields
365+
self.fields()
384366
.iter()
385367
.filter(|field| field.attrs.ignore.is_active())
386368
}
@@ -392,15 +374,14 @@ impl<'a> ReflectStruct<'a> {
392374
.collect()
393375
}
394376

395-
/// Get an iterator of fields which are ignored by the reflection API
377+
/// Get an iterator of fields which are ignored by the reflection API.
396378
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
397-
self.fields
379+
self.fields()
398380
.iter()
399381
.filter(|field| field.attrs.ignore.is_ignored())
400382
}
401383

402384
/// The complete set of fields in this struct.
403-
#[allow(dead_code)]
404385
pub fn fields(&self) -> &[StructField<'a>] {
405386
&self.fields
406387
}
@@ -414,6 +395,26 @@ impl<'a> ReflectStruct<'a> {
414395
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync },
415396
}
416397
}
398+
399+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
400+
///
401+
/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method
402+
pub fn get_type_registration(
403+
&self,
404+
where_clause_options: &WhereClauseOptions,
405+
) -> proc_macro2::TokenStream {
406+
let reflect_path = self.meta.bevy_reflect_path();
407+
408+
crate::registration::impl_get_type_registration(
409+
self.meta.type_name(),
410+
reflect_path,
411+
self.meta.traits().idents(),
412+
self.meta.generics(),
413+
where_clause_options,
414+
Some(&self.serialization_denylist),
415+
Some(self.active_types().iter()),
416+
)
417+
}
417418
}
418419

419420
impl<'a> ReflectEnum<'a> {
@@ -472,6 +473,19 @@ impl<'a> ReflectEnum<'a> {
472473
ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync + #FQDefault },
473474
}
474475
}
476+
477+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
478+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
479+
crate::registration::impl_get_type_registration(
480+
self.meta.type_name,
481+
&self.meta.bevy_reflect_path,
482+
self.meta.traits.idents(),
483+
self.meta.generics,
484+
&self.where_clause_options(),
485+
None,
486+
Some(self.active_fields().map(|field| &field.data.ty)),
487+
)
488+
}
475489
}
476490

477491
impl<'a> EnumVariant<'a> {

crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
8888
bevy_reflect_path,
8989
);
9090

91-
let get_type_registration_impl = reflect_enum
92-
.meta()
93-
.get_type_registration(&where_clause_options);
91+
let get_type_registration_impl = reflect_enum.get_type_registration();
9492
let (impl_generics, ty_generics, where_clause) =
9593
reflect_enum.meta().generics().split_for_impl();
9694

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,27 @@ use crate::utility::{extend_where_clause, WhereClauseOptions};
44
use bit_set::BitSet;
55
use proc_macro2::Ident;
66
use quote::quote;
7-
use syn::{Generics, Path};
7+
use syn::{Generics, Path, Type};
88

99
/// Creates the `GetTypeRegistration` impl for the given type data.
1010
#[allow(clippy::too_many_arguments)]
11-
pub(crate) fn impl_get_type_registration(
11+
pub(crate) fn impl_get_type_registration<'a>(
1212
type_name: &Ident,
1313
bevy_reflect_path: &Path,
1414
registration_data: &[Ident],
1515
generics: &Generics,
1616
where_clause_options: &WhereClauseOptions,
1717
serialization_denylist: Option<&BitSet<u32>>,
18+
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
1819
) -> proc_macro2::TokenStream {
20+
let type_deps_fn = type_dependencies.map(|deps| {
21+
quote! {
22+
fn register_type_dependencies(registry: &mut #bevy_reflect_path::__macro_exports::TypeRegistry) {
23+
#(registry.try_register::<#deps>();)*
24+
}
25+
}
26+
});
27+
1928
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
2029
let serialization_data = serialization_denylist.map(|denylist| {
2130
let denylist = denylist.into_iter();
@@ -37,6 +46,8 @@ pub(crate) fn impl_get_type_registration(
3746
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
3847
registration
3948
}
49+
50+
#type_deps_fn
4051
}
4152
}
4253
}

crates/bevy_reflect/src/enums/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ mod tests {
316316
#[test]
317317
fn enum_should_allow_generics() {
318318
#[derive(Reflect, Debug, PartialEq)]
319-
enum TestEnum<T: FromReflect> {
319+
enum TestEnum<T: FromReflect + GetTypeRegistration> {
320320
A,
321321
B(T),
322322
C { value: T },

crates/bevy_reflect/src/impls/std.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::std_traits::ReflectDefault;
2-
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectOwned};
2+
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectOwned, TypeRegistry};
33
use crate::{
44
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
55
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
@@ -315,12 +315,16 @@ macro_rules! impl_reflect_for_veclike {
315315
}
316316
}
317317

318-
impl<T: FromReflect> GetTypeRegistration for $ty {
318+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for $ty {
319319
fn get_type_registration() -> TypeRegistration {
320320
let mut registration = TypeRegistration::of::<$ty>();
321321
registration.insert::<ReflectFromPtr>(FromType::<$ty>::from_type());
322322
registration
323323
}
324+
325+
fn register_type_dependencies(registry: &mut TypeRegistry) {
326+
registry.try_register::<T>();
327+
}
324328
}
325329

326330
impl<T: FromReflect> FromReflect for $ty {
@@ -505,14 +509,19 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
505509

506510
impl<K, V> GetTypeRegistration for HashMap<K, V>
507511
where
508-
K: FromReflect + Eq + Hash,
509-
V: FromReflect,
512+
K: FromReflect + GetTypeRegistration + Eq + Hash,
513+
V: FromReflect + GetTypeRegistration,
510514
{
511515
fn get_type_registration() -> TypeRegistration {
512516
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
513517
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
514518
registration
515519
}
520+
521+
fn register_type_dependencies(registry: &mut TypeRegistry) {
522+
registry.try_register::<K>();
523+
registry.try_register::<V>();
524+
}
516525
}
517526

518527
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
@@ -671,10 +680,14 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
671680
macro_rules! impl_array_get_type_registration {
672681
($($N:expr)+) => {
673682
$(
674-
impl<T: Reflect > GetTypeRegistration for [T; $N] {
683+
impl<T: Reflect + GetTypeRegistration> GetTypeRegistration for [T; $N] {
675684
fn get_type_registration() -> TypeRegistration {
676685
TypeRegistration::of::<[T; $N]>()
677686
}
687+
688+
fn register_type_dependencies(registry: &mut TypeRegistry) {
689+
registry.try_register::<T>();
690+
}
678691
}
679692
)+
680693
};
@@ -687,10 +700,14 @@ impl_array_get_type_registration! {
687700
30 31 32
688701
}
689702

690-
impl<T: FromReflect> GetTypeRegistration for Option<T> {
703+
impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for Option<T> {
691704
fn get_type_registration() -> TypeRegistration {
692705
TypeRegistration::of::<Option<T>>()
693706
}
707+
708+
fn register_type_dependencies(registry: &mut TypeRegistry) {
709+
registry.try_register::<T>();
710+
}
694711
}
695712

696713
impl<T: FromReflect> Enum for Option<T> {

crates/bevy_reflect/src/lib.rs

+96-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub use erased_serde;
6868
pub mod __macro_exports {
6969
use crate::Uuid;
7070

71+
pub use crate::TypeRegistry;
72+
7173
/// Generates a new UUID from the given UUIDs `a` and `b`,
7274
/// where the bytes are generated by a bitwise `a ^ b.rotate_right(1)`.
7375
/// The generated UUID will be a `UUIDv4` (meaning that the bytes should be random, not e.g. derived from the system time).
@@ -479,6 +481,99 @@ mod tests {
479481
assert_eq!(new_foo, expected_new_foo);
480482
}
481483

484+
#[test]
485+
fn should_auto_register_fields() {
486+
#[derive(Reflect, FromReflect)]
487+
struct Foo {
488+
bar: Bar,
489+
}
490+
491+
#[derive(Reflect, FromReflect)]
492+
enum Bar {
493+
Variant(Baz),
494+
}
495+
496+
#[derive(Reflect, FromReflect)]
497+
struct Baz(usize);
498+
499+
// === Basic === //
500+
let mut registry = TypeRegistry::empty();
501+
registry.register::<Foo>();
502+
503+
assert!(
504+
registry.contains(TypeId::of::<Bar>()),
505+
"registry should contain auto-registered `Bar` from `Foo`"
506+
);
507+
508+
// === Option === //
509+
let mut registry = TypeRegistry::empty();
510+
registry.register::<Option<Foo>>();
511+
512+
assert!(
513+
registry.contains(TypeId::of::<Bar>()),
514+
"registry should contain auto-registered `Bar` from `Option<Foo>`"
515+
);
516+
517+
// === Tuple === //
518+
let mut registry = TypeRegistry::empty();
519+
registry.register::<(Foo, Foo)>();
520+
521+
assert!(
522+
registry.contains(TypeId::of::<Bar>()),
523+
"registry should contain auto-registered `Bar` from `(Foo, Foo)`"
524+
);
525+
526+
// === Array === //
527+
let mut registry = TypeRegistry::empty();
528+
registry.register::<[Foo; 3]>();
529+
530+
assert!(
531+
registry.contains(TypeId::of::<Bar>()),
532+
"registry should contain auto-registered `Bar` from `[Foo; 3]`"
533+
);
534+
535+
// === Vec === //
536+
let mut registry = TypeRegistry::empty();
537+
registry.register::<Vec<Foo>>();
538+
539+
assert!(
540+
registry.contains(TypeId::of::<Bar>()),
541+
"registry should contain auto-registered `Bar` from `Vec<Foo>`"
542+
);
543+
544+
// === HashMap === //
545+
let mut registry = TypeRegistry::empty();
546+
registry.register::<HashMap<i32, Foo>>();
547+
548+
assert!(
549+
registry.contains(TypeId::of::<Bar>()),
550+
"registry should contain auto-registered `Bar` from `HashMap<i32, Foo>`"
551+
);
552+
}
553+
554+
#[test]
555+
fn should_not_auto_register_existing_types() {
556+
#[derive(Reflect)]
557+
struct Foo {
558+
bar: Bar,
559+
}
560+
561+
#[derive(Reflect, Default)]
562+
struct Bar(usize);
563+
564+
let mut registry = TypeRegistry::empty();
565+
registry.register::<Bar>();
566+
registry.register_type_data::<Bar, ReflectDefault>();
567+
registry.register::<Foo>();
568+
569+
assert!(
570+
registry
571+
.get_type_data::<ReflectDefault>(TypeId::of::<Bar>())
572+
.is_some(),
573+
"registry should contain existing registration for `Bar`"
574+
);
575+
}
576+
482577
#[test]
483578
fn reflect_serialize() {
484579
#[derive(Reflect)]
@@ -700,7 +795,7 @@ mod tests {
700795

701796
// Struct (generic)
702797
#[derive(Reflect)]
703-
struct MyGenericStruct<T: Reflect> {
798+
struct MyGenericStruct<T: Reflect + GetTypeRegistration> {
704799
foo: T,
705800
bar: usize,
706801
}

0 commit comments

Comments
 (0)