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 132dc36dbaf889..a3e8082211b7d0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -90,7 +90,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, @@ -234,19 +241,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 20523b8c5ba6b2..cea302b941f5c6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -169,7 +169,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()}, @@ -198,8 +198,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 2f2f5cf5e3582f..1982870a711889 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -351,7 +351,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 2b7fd1815c6073..d17cf2991bf7b1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -20,12 +20,12 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { .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 c74d4d9064f808..af7c2eb136e377 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 @@ -15,7 +15,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { 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 40a20a40fe3dda..1cef6ea0ad83e6 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -721,6 +721,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, FromReflect, 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]