diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 27a298044ebaa5..46ec6f60d4a943 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -95,7 +95,14 @@ pub(crate) struct StructField<'a> { /// The reflection-based attributes on the field. pub attrs: ReflectFieldAttr, /// The index of this field within the struct. - pub index: usize, + pub declaration_index: usize, + /// The index of this field as seen by the reflection API. + /// + /// This index accounts for the removal of [ignored] fields. + /// It will only be `Some(index)` when the field is not ignored. + /// + /// [ignored]: crate::field_attributes::ReflectIgnoreBehavior::IgnoreAlways + pub reflection_index: Option, /// The documentation for this field, if any #[cfg(feature = "documentation")] pub doc: crate::documentation::Documentation, @@ -308,19 +315,31 @@ impl<'a> ReflectDerive<'a> { } fn collect_struct_fields(fields: &'a Fields) -> Result>, syn::Error> { + let mut active_index = 0; let sifter: utility::ResultSifter> = fields .iter() .enumerate() - .map(|(index, field)| -> Result { - let attrs = parse_field_attrs(&field.attrs)?; - Ok(StructField { - index, - attrs, - data: field, - #[cfg(feature = "documentation")] - doc: crate::documentation::Documentation::from_attributes(&field.attrs), - }) - }) + .map( + |(declaration_index, field)| -> Result { + let attrs = parse_field_attrs(&field.attrs)?; + + let reflection_index = if attrs.ignore.is_ignored() { + None + } else { + active_index += 1; + Some(active_index - 1) + }; + + Ok(StructField { + declaration_index, + reflection_index, + attrs, + data: field, + #[cfg(feature = "documentation")] + doc: crate::documentation::Documentation::from_attributes(&field.attrs), + }) + }, + ) .fold( utility::ResultSifter::default(), utility::ResultSifter::fold, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index 00ae55339f805a..a86823cc5ec73a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -189,7 +189,7 @@ fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair { reflect_struct .ignored_fields() .map(|field| { - let member = ident_or_index(field.data.ident.as_ref(), field.index); + let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index); let value = match &field.attrs.default { DefaultBehavior::Func(path) => quote! {#path()}, @@ -218,8 +218,12 @@ fn get_active_fields( reflect_struct .active_fields() .map(|field| { - let member = ident_or_index(field.data.ident.as_ref(), field.index); - let accessor = get_field_accessor(field.data, field.index, is_tuple); + let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index); + let accessor = get_field_accessor( + field.data, + field.reflection_index.expect("field should be active"), + is_tuple, + ); let ty = field.data.ty.clone(); let get_field = quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 19b6bebbd78ecd..fed30ec059a305 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -353,7 +353,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden // Ignored field continue; } - constructor_argument.push(generate_for_field(reflect_idx, field.index, field)); + constructor_argument.push(generate_for_field(reflect_idx, field.declaration_index, field)); reflect_idx += 1; } constructor_argument diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index 40ca5e3bd503b3..7792ad5bedd2b6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -19,12 +19,12 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .ident .as_ref() .map(|i| i.to_string()) - .unwrap_or_else(|| field.index.to_string()) + .unwrap_or_else(|| field.declaration_index.to_string()) }) .collect::>(); let field_idents = reflect_struct .active_fields() - .map(|field| ident_or_index(field.data.ident.as_ref(), field.index)) + .map(|field| ident_or_index(field.data.ident.as_ref(), field.declaration_index)) .collect::>(); let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index 37984fc1f08d73..07cc3825428843 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -14,7 +14,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: let field_idents = reflect_struct .active_fields() - .map(|field| Member::Unnamed(Index::from(field.index))) + .map(|field| Member::Unnamed(Index::from(field.declaration_index))) .collect::>(); let field_types = reflect_struct.active_types(); let field_count = field_idents.len(); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index b8ba8da5dffddd..426290b8db738a 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -764,6 +764,39 @@ mod tests { .unwrap_or_default()); } + #[test] + fn from_reflect_should_allow_ignored_unnamed_fields() { + #[derive(Reflect, FromReflect, Eq, PartialEq, Debug)] + struct MyTupleStruct(i8, #[reflect(ignore)] i16, i32); + + let expected = MyTupleStruct(1, 0, 3); + + let mut dyn_tuple_struct = DynamicTupleStruct::default(); + dyn_tuple_struct.insert(1_i8); + dyn_tuple_struct.insert(3_i32); + let my_tuple_struct = ::from_reflect(&dyn_tuple_struct); + + assert_eq!(Some(expected), my_tuple_struct); + + #[derive(Reflect, FromReflect, Eq, PartialEq, Debug)] + enum MyEnum { + Tuple(i8, #[reflect(ignore)] i16, i32), + } + + let expected = MyEnum::Tuple(1, 0, 3); + + let mut dyn_tuple = DynamicTuple::default(); + dyn_tuple.insert(1_i8); + dyn_tuple.insert(3_i32); + + let mut dyn_enum = DynamicEnum::default(); + dyn_enum.set_variant("Tuple", dyn_tuple); + + let my_enum = ::from_reflect(&dyn_enum); + + assert_eq!(Some(expected), my_enum); + } + #[test] fn from_reflect_should_use_default_field_attributes() { #[derive(Reflect, Eq, PartialEq, Debug)] diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index 58231b2654ac2a..e496775480167a 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -12,7 +12,7 @@ mod tests { use crate::{ serde::{ReflectSerializer, UntypedReflectDeserializer}, type_registry::TypeRegistry, - DynamicStruct, Reflect, + DynamicStruct, FromReflect, Reflect, }; use serde::de::DeserializeSeed; @@ -60,7 +60,7 @@ mod tests { #[test] fn test_serialization_tuple_struct() { - #[derive(Debug, Reflect, PartialEq)] + #[derive(Debug, Reflect, FromReflect, PartialEq)] #[reflect(PartialEq)] struct TestStruct( i32, @@ -87,10 +87,12 @@ mod tests { let value = reflect_deserializer.deserialize(&mut deserializer).unwrap(); let deserialized = value.take::().unwrap(); - assert!( - expected.reflect_partial_eq(&deserialized).unwrap(), - "Expected {expected:?} found {deserialized:?}" - ); + println!("{:?}", deserialized); + + let expected = TestStruct(3, 0, 0, 6); + let received = ::from_reflect(&deserialized).unwrap(); + + assert_eq!(expected, received); } #[test]