From 0c65b8700730691cb192925367cbedd6fc4dc1f3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 May 2022 23:38:03 -0700 Subject: [PATCH 01/20] Cleaned up reflection macros --- .../src/container_attributes.rs | 155 +++ .../bevy_reflect_derive/src/derive_data.rs | 191 +++ .../src/field_attributes.rs | 74 ++ .../bevy_reflect_derive/src/from_reflect.rs | 248 ++-- .../bevy_reflect_derive/src/impls.rs | 387 ++++++ .../bevy_reflect_derive/src/lib.rs | 1036 +---------------- .../bevy_reflect_derive/src/reflect_value.rs | 40 + .../bevy_reflect_derive/src/registration.rs | 22 + crates/bevy_reflect/src/lib.rs | 2 +- 9 files changed, 1057 insertions(+), 1098 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/registration.rs diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs new file mode 100644 index 0000000000000..2d3e6fbfd347d --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -0,0 +1,155 @@ +use proc_macro2::{Ident, Span}; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{Meta, NestedMeta, Path}; + +#[derive(Clone)] +pub enum TraitImpl { + NotImplemented, + Implemented, + Custom(Ident), +} + +impl Default for TraitImpl { + fn default() -> Self { + Self::NotImplemented + } +} + +#[derive(Default)] +pub struct ReflectAttrs { + reflect_hash: TraitImpl, + pub(crate) reflect_partial_eq: TraitImpl, + serialize: TraitImpl, + data: Vec, +} + +impl ReflectAttrs { + pub fn from_nested_metas(nested_metas: &Punctuated) -> Self { + let mut attrs = ReflectAttrs::default(); + for nested_meta in nested_metas.iter() { + match nested_meta { + NestedMeta::Lit(_) => {} + NestedMeta::Meta(meta) => match meta { + Meta::Path(path) => { + if let Some(segment) = path.segments.iter().next() { + let ident = segment.ident.to_string(); + match ident.as_str() { + "PartialEq" => attrs.reflect_partial_eq = TraitImpl::Implemented, + "Hash" => attrs.reflect_hash = TraitImpl::Implemented, + "Serialize" => attrs.serialize = TraitImpl::Implemented, + _ => attrs.data.push(Ident::new( + &format!("Reflect{}", segment.ident), + Span::call_site(), + )), + } + } + } + Meta::List(list) => { + let ident = if let Some(segment) = list.path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + if let Some(list_nested) = list.nested.iter().next() { + match list_nested { + NestedMeta::Meta(list_nested_meta) => match list_nested_meta { + Meta::Path(path) => { + if let Some(segment) = path.segments.iter().next() { + match ident.as_str() { + "PartialEq" => { + attrs.reflect_partial_eq = + TraitImpl::Custom(segment.ident.clone()); + } + "Hash" => { + attrs.reflect_hash = + TraitImpl::Custom(segment.ident.clone()); + } + "Serialize" => { + attrs.serialize = + TraitImpl::Custom(segment.ident.clone()); + } + _ => {} + } + } + } + Meta::List(_) => {} + Meta::NameValue(_) => {} + }, + NestedMeta::Lit(_) => {} + } + } + } + Meta::NameValue(_) => {} + }, + } + } + + attrs + } + + pub fn data(&self) -> &[Ident] { + &self.data + } + + pub fn get_hash_impl(&self, path: &Path) -> proc_macro2::TokenStream { + match &self.reflect_hash { + TraitImpl::Implemented => quote! { + use std::hash::{Hash, Hasher}; + let mut hasher = #path::ReflectHasher::default(); + Hash::hash(&std::any::Any::type_id(self), &mut hasher); + Hash::hash(self, &mut hasher); + Some(hasher.finish()) + }, + TraitImpl::Custom(impl_fn) => quote! { + Some(#impl_fn(self)) + }, + TraitImpl::NotImplemented => quote! { + None + }, + } + } + + pub fn get_partial_eq_impl(&self) -> proc_macro2::TokenStream { + match &self.reflect_partial_eq { + TraitImpl::Implemented => quote! { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + Some(std::cmp::PartialEq::eq(self, value)) + } else { + Some(false) + } + }, + TraitImpl::Custom(impl_fn) => quote! { + Some(#impl_fn(self, value)) + }, + TraitImpl::NotImplemented => quote! { + None + }, + } + } + + pub fn get_serialize_impl(&self, path: &Path) -> proc_macro2::TokenStream { + match &self.serialize { + TraitImpl::Implemented => quote! { + Some(#path::serde::Serializable::Borrowed(self)) + }, + TraitImpl::Custom(impl_fn) => quote! { + Some(#impl_fn(self)) + }, + TraitImpl::NotImplemented => quote! { + None + }, + } + } +} + +impl Parse for ReflectAttrs { + fn parse(input: ParseStream) -> syn::Result { + let result = Punctuated::::parse_terminated(input)?; + Ok(ReflectAttrs::from_nested_metas(&result)) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs new file mode 100644 index 0000000000000..f65be24c86158 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -0,0 +1,191 @@ +use crate::container_attributes::ReflectAttrs; +use crate::field_attributes::{ + parse_field_attrs, ReflectFieldAttr, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, +}; +use crate::get_bevy_reflect_path; +use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; + +pub enum DeriveType { + Struct, + TupleStruct, + UnitStruct, + Value, +} + +/// Data used by derive macros for `Reflect` and `FromReflect` +/// +/// # Example +/// ```ignore +/// // attrs +/// // |----------------------------------------| +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// // type_name generics +/// // |-------------------||----------| +/// struct ThingThatImReflecting { +/// x: T1, // | +/// y: T2, // |- fields +/// z: T3 // | +/// } +/// ``` +pub struct ReflectDeriveData<'a> { + derive_type: DeriveType, + attrs: ReflectAttrs, + type_name: &'a Ident, + generics: &'a Generics, + fields: Vec<(&'a Field, ReflectFieldAttr, usize)>, + bevy_reflect_path: Path, +} + +impl<'a> ReflectDeriveData<'a> { + pub fn from_input(input: &'a DeriveInput) -> Result { + let mut output = Self { + type_name: &input.ident, + derive_type: DeriveType::Value, + generics: &input.generics, + fields: Vec::new(), + attrs: ReflectAttrs::default(), + bevy_reflect_path: get_bevy_reflect_path(), + }; + + // Should indicate whether `#[reflect_value]` was used + let mut force_reflect_value = false; + + for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + let meta_list = if let Meta::List(meta_list) = attribute { + meta_list + } else { + continue; + }; + + if let Some(ident) = meta_list.path.get_ident() { + if ident == REFLECT_ATTRIBUTE_NAME { + output.attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); + } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { + force_reflect_value = true; + output.attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); + } + } + } + + let fields = match &input.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::Struct; + } + &fields.named + } + Data::Struct(DataStruct { + fields: Fields::Unnamed(fields), + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::TupleStruct; + } + &fields.unnamed + } + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::UnitStruct; + } + return Ok(output); + } + _ => { + return Ok(output); + } + }; + + let mut errors: Option = None; + output.fields = fields + .iter() + .enumerate() + .map(|(i, f)| { + ( + f, + parse_field_attrs(&f.attrs).unwrap_or_else(|err| { + if let Some(ref mut error) = errors { + error.combine(err); + } else { + errors = Some(err); + } + ReflectFieldAttr::default() + }), + i, + ) + }) + .collect::>(); + + if let Some(errs) = errors { + return Err(errs); + } + + Ok(output) + } + + /// Get an iterator over the active fields + pub fn active_fields(&self) -> impl Iterator { + self.fields + .iter() + .filter(|(_field, attrs, _i)| !attrs.ignore.unwrap_or(false)) + } + + /// Get an iterator over the ignored fields + pub fn ignored_fields(&self) -> impl Iterator { + self.fields + .iter() + .filter(|(_field, attrs, _i)| attrs.ignore.unwrap_or(false)) + } + + /// Get a collection of all active types + pub fn active_types(&self) -> Vec { + self.active_fields() + .map(|(field, ..)| field.ty.clone()) + .collect::>() + } + + /// The [`DeriveType`] of this struct. + pub fn derive_type(&self) -> &DeriveType { + &self.derive_type + } + + /// The list of reflect-related attributes on this struct. + pub fn attrs(&self) -> &ReflectAttrs { + &self.attrs + } + + /// The name of this struct. + pub fn type_name(&self) -> &'a Ident { + self.type_name + } + + /// The generics associated with this struct. + pub fn generics(&self) -> &'a Generics { + self.generics + } + + /// The complete set of fields in this struct. + #[allow(dead_code)] + pub fn fields(&self) -> &Vec<(&'a Field, ReflectFieldAttr, usize)> { + &self.fields + } + + /// The cached `bevy_reflect` path. + pub fn bevy_reflect_path(&self) -> &Path { + &self.bevy_reflect_path + } + + /// Returns the `GetTypeRegistration` impl as a `TokenStream`. + pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + crate::registration::impl_get_type_registration( + self.type_name, + &self.bevy_reflect_path, + self.attrs.data(), + self.generics, + ) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs new file mode 100644 index 0000000000000..a4a589d44d620 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -0,0 +1,74 @@ +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{Attribute, Meta, NestedMeta}; + +pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; +pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +pub(crate) static IGNORE: &str = "ignore"; + +/// A container for reflection field configuration. +#[derive(Default)] +pub struct ReflectFieldAttr { + /// Determines if this field should be ignored. + pub ignore: Option, +} + +/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`). +pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result { + let mut args = ReflectFieldAttr::default(); + let mut errors: Option = None; + + let attrs = attrs + .iter() + .filter(|a| a.path.is_ident(REFLECT_ATTRIBUTE_NAME)); + for attr in attrs { + let meta = attr.parse_meta()?; + if let Err(err) = parse_meta(&mut args, &meta) { + if let Some(ref mut error) = errors { + error.combine(err); + } else { + errors = Some(err); + } + } + } + + if let Some(error) = errors { + Err(error) + } else { + Ok(args) + } +} + +fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error> { + match meta { + Meta::Path(path) => { + if path.is_ident(IGNORE) { + args.ignore = Some(true); + } else { + return Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )); + } + } + Meta::NameValue(pair) => { + let path = &pair.path; + return Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )); + } + Meta::List(list) => { + if !list.path.is_ident(REFLECT_ATTRIBUTE_NAME) { + return Err(syn::Error::new(list.path.span(), "unexpected property")); + } + for nested in list.nested.iter() { + if let NestedMeta::Meta(meta) = nested { + parse_meta(args, meta)?; + } + } + } + } + + Ok(()) +} 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 aabf75d904479..2afc47aefd713 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,130 +1,84 @@ +use crate::ReflectDeriveData; use proc_macro::TokenStream; +use proc_macro2::Span; use quote::quote; use syn::{Field, Generics, Ident, Index, Member, Path}; -pub fn impl_struct( - struct_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], - custom_constructor: Option, -) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); +/// Implements `FromReflect` for the given struct +pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, false) +} + +/// Implements `FromReflect` for the given tuple struct +pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, true) +} +/// Implements `FromReflect` for the given value type +pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) + } + } + }) +} - // Add FromReflect bound for each active field - let mut where_from_reflect_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if field_count > 0 { - quote! {where} +/// Container for a struct's members (field name or index) and their +/// corresponding values. +struct StructFields(Vec, Vec); + +impl StructFields { + pub fn new(items: (Vec, Vec)) -> Self { + Self(items.0, items.1) + } +} + +fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> TokenStream { + let struct_name = derive_data.type_name(); + let generics = derive_data.generics(); + let bevy_reflect_path = derive_data.bevy_reflect_path(); + + let ref_struct = Ident::new("__ref_struct", Span::call_site()); + let ref_struct_type = if is_tuple { + Ident::new("TupleStruct", Span::call_site()) } else { - quote! {} + Ident::new("Struct", Span::call_site()) }; - where_from_reflect_clause.extend(quote! { - #(#field_types: #bevy_reflect_path::FromReflect,)* - }); - let constructor = if let Some(constructor) = custom_constructor { + let field_types = derive_data.active_types(); + let StructFields(ignored_members, ignored_values) = get_ignored_fields(derive_data, is_tuple); + let StructFields(active_members, active_values) = + get_active_fields(derive_data, &ref_struct, is_tuple); + + let default_ident = Ident::new("ReflectDefault", Span::call_site()); + let constructor = if derive_data.attrs().data().contains(&default_ident) { quote!( - let mut value: Self = #constructor; + let mut __this = Self::default(); #( - value.#field_idents = { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - }; + __this.#active_members = #active_values; )* - Some(value) + Some(__this) ) } else { quote!( Some( Self { - #(#field_idents: { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - },)* - #(#ignored_field_idents: Default::default(),)* + #(#active_members: #active_values,)* + #(#ignored_members: #ignored_values,)* } ) ) }; - TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause - { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() { - #constructor - } else { - None - } - } - } - }) -} - -pub fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Add FromReflect bound for each active field let mut where_from_reflect_clause = if where_clause.is_some() { quote! {#where_clause} - } else if field_count > 0 { + } else if !active_members.is_empty() { quote! {where} } else { quote! {} @@ -137,16 +91,9 @@ pub fn impl_tuple_struct( impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() { - Some( - Self{ - #(#field_idents: - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)? - ,)* - #(#ignored_field_idents: Default::default(),)* - } - ) + use #bevy_reflect_path::#ref_struct_type; + if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = reflect.reflect_ref() { + #constructor } else { None } @@ -155,13 +102,76 @@ pub fn impl_tuple_struct( }) } -pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) - } - } - }) +/// Get the collection of ignored field definitions +/// +/// Each item in the collection takes the form: `field_ident: field_value`. +fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> StructFields { + StructFields::new( + derive_data + .ignored_fields() + .map(|(field, _attr, index)| { + let member = get_ident(field, *index, is_tuple); + let value = quote! { + Default::default() + }; + + (member, value) + }) + .unzip(), + ) +} + +/// Get the collection of active field definitions +/// +/// Each item in the collection takes the form: `field_ident: field_value`. +fn get_active_fields( + derive_data: &ReflectDeriveData, + dyn_struct_name: &Ident, + is_tuple: bool, +) -> StructFields { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + + StructFields::new( + derive_data + .active_fields() + .map(|(field, _attr, index)| { + let member = get_ident(field, *index, is_tuple); + let ty = field.ty.clone(); + + // Accesses the field on the given dynamic struct or tuple struct + let get_field = if is_tuple { + quote! { + #dyn_struct_name.field(#index) + } + } else { + let name = field + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| index.to_string()); + quote! { + #dyn_struct_name.field(#name) + } + }; + + let value = quote! { { + <#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)? + }}; + + (member, value) + }) + .unzip(), + ) +} + +fn get_ident(field: &Field, index: usize, is_tuple: bool) -> Member { + if is_tuple { + Member::Unnamed(Index::from(index)) + } else { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(index))) + } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs new file mode 100644 index 0000000000000..50e5f320c7de9 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -0,0 +1,387 @@ +use crate::container_attributes::{ReflectAttrs, TraitImpl}; +use crate::ReflectDeriveData; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Index, Member, Path}; + +pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + let struct_name = derive_data.type_name(); + + let field_names = derive_data + .active_fields() + .map(|(field, _attr, index)| { + field + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| index.to_string()) + }) + .collect::>(); + let field_idents = derive_data + .active_fields() + .map(|(field, _attr, index)| { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) + }) + .collect::>(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data.attrs().get_hash_impl(bevy_reflect_path); + let serialize_fn = derive_data.attrs().get_serialize_impl(bevy_reflect_path); + let partial_eq_fn = match derive_data.attrs().reflect_partial_eq { + TraitImpl::NotImplemented => quote! { + use #bevy_reflect_path::Struct; + #bevy_reflect_path::struct_partial_eq(self, value) + }, + TraitImpl::Implemented | TraitImpl::Custom(_) => derive_data.attrs().get_partial_eq_impl(), + }; + + let get_type_registration_impl = derive_data.get_type_registration(); + let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* + dynamic + } + } + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + let name = struct_value.name_at(i).unwrap(); + #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-struct type to struct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Struct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Struct(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + let struct_name = derive_data.type_name(); + let get_type_registration_impl = derive_data.get_type_registration(); + + let field_idents = derive_data + .active_fields() + .map(|(_field, _attr, index)| Member::Unnamed(Index::from(*index))) + .collect::>(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data.attrs().get_hash_impl(bevy_reflect_path); + let serialize_fn = derive_data.attrs().get_serialize_impl(bevy_reflect_path); + let partial_eq_fn = match derive_data.attrs().reflect_partial_eq { + TraitImpl::NotImplemented => quote! { + use #bevy_reflect_path::TupleStruct; + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + }, + TraitImpl::Implemented | TraitImpl::Custom(_) => derive_data.attrs().get_partial_eq_impl(), + }; + + let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { + #bevy_reflect_path::TupleStructFieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { + let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(self.#field_idents.clone_value());)* + dynamic + } + } + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + use #bevy_reflect_path::TupleStruct; + Box::new(self.clone_dynamic()) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + use #bevy_reflect_path::TupleStruct; + if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + self.field_mut(i).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::TupleStruct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::TupleStruct(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +pub fn impl_value( + type_name: &Ident, + generics: &Generics, + get_type_registration_impl: proc_macro2::TokenStream, + bevy_reflect_path: &Path, + reflect_attrs: &ReflectAttrs, +) -> TokenStream { + let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); + let partial_eq_fn = reflect_attrs.get_partial_eq_impl(); + let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_impl + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Value is not {}.", std::any::type_name::()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Value(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Value(self) + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 2b5f93a85edd4..3de1dccb7ea47 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -1,602 +1,60 @@ extern crate proc_macro; +mod container_attributes; +mod derive_data; +mod field_attributes; mod from_reflect; +mod impls; mod reflect_trait; +mod reflect_value; +mod registration; mod type_uuid; +use crate::derive_data::ReflectDeriveData; use bevy_macro_utils::BevyManifest; +use derive_data::DeriveType; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::{Comma, Paren, Where}, - Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, NestedMeta, - Path, -}; - -#[derive(Default)] -struct PropAttributeArgs { - pub ignore: Option, -} - -#[derive(Clone)] -enum TraitImpl { - NotImplemented, - Implemented, - Custom(Ident), -} - -impl Default for TraitImpl { - fn default() -> Self { - Self::NotImplemented - } -} - -enum DeriveType { - Struct, - TupleStruct, - UnitStruct, - Value, -} - -static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; -static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +use reflect_value::ReflectValueDef; +use syn::{parse_macro_input, DeriveInput, Path}; #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - let mut reflect_attrs = ReflectAttrs::default(); - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME { - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( - type_name, - &bevy_reflect_path, - registration_data, - &ast.generics, - ); - - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => impl_struct( - type_name, - &ast.generics, - &get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::TupleStruct => impl_tuple_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::Value => impl_value( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - ), - } -} - -fn impl_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: &proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::Struct; - #bevy_reflect_path::struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), - }; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - TokenStream::from(quote! { - #get_type_registration_impl - - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { - fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - fn name_at(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { - #bevy_reflect_path::FieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { - let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* - dynamic - } - } - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - let name = struct_value.name_at(i).unwrap(); - #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-struct type to struct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Struct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Struct(self) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) -} - -fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::TupleStruct; - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics { - fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { - #bevy_reflect_path::TupleStructFieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { - let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(self.#field_idents.clone_value());)* - dynamic - } - } - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - use #bevy_reflect_path::TupleStruct; - Box::new(self.clone_dynamic()) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - self.field_mut(i).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::TupleStruct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::TupleStruct(self) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) -} - -fn impl_value( - type_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, -) -> TokenStream { - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_attrs.get_partial_eq_impl(); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); - } else { - panic!("Value is not {}.", std::any::type_name::()); - } - } - - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Value(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Value(self) - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - } - }) -} -struct ReflectDef { - type_name: Ident, - generics: Generics, - attrs: Option, -} - -impl Parse for ReflectDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let type_ident = input.parse::()?; - let generics = input.parse::()?; - let mut lookahead = input.lookahead1(); - let mut where_clause = None; - if lookahead.peek(Where) { - where_clause = Some(input.parse()?); - lookahead = input.lookahead1(); - } - - let mut attrs = None; - if lookahead.peek(Paren) { - let content; - parenthesized!(content in input); - attrs = Some(content.parse::()?); - } - - Ok(ReflectDef { - type_name: type_ident, - generics: Generics { - where_clause, - ..generics - }, - attrs, - }) + match derive_data.derive_type() { + DeriveType::Struct | DeriveType::UnitStruct => impls::impl_struct(&derive_data), + DeriveType::TupleStruct => impls::impl_tuple_struct(&derive_data), + DeriveType::Value => impls::impl_value( + derive_data.type_name(), + derive_data.generics(), + derive_data.get_type_registration(), + derive_data.bevy_reflect_path(), + derive_data.attrs(), + ), } } #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let bevy_reflect_path = get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( + let registration_data = &reflect_attrs.data(); + let get_type_registration_impl = registration::impl_get_type_registration( ty, &bevy_reflect_path, registration_data, &reflect_value_def.generics, ); - impl_value( + impls::impl_value( ty, &reflect_value_def.generics, get_type_registration_impl, @@ -605,76 +63,6 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { ) } -/// Represents the information needed to implement a type as a Reflect Struct. -/// -/// # Example -/// ```ignore -/// impl_reflect_struct!( -/// // attrs -/// // |----------------------------------------| -/// #[reflect(PartialEq, Serialize, Deserialize, Default)] -/// // type_name generics -/// // |-------------------||----------| -/// struct ThingThatImReflecting { -/// x: T1, // | -/// y: T2, // |- fields -/// z: T3 // | -/// } -/// ); -/// ``` -struct ReflectStructDef { - type_name: Ident, - generics: Generics, - attrs: ReflectAttrs, - fields: Fields, -} - -impl Parse for ReflectStructDef { - fn parse(input: ParseStream) -> syn::Result { - let ast = input.parse::()?; - - let type_name = ast.ident; - let generics = ast.generics; - let fields = match ast.data { - Data::Struct(data) => data.fields, - Data::Enum(data) => { - return Err(syn::Error::new_spanned( - data.enum_token, - "Enums are not currently supported for reflection", - )) - } - Data::Union(data) => { - return Err(syn::Error::new_spanned( - data.union_token, - "Unions are not supported for reflection", - )) - } - }; - - let mut attrs = ReflectAttrs::default(); - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME || ident == REFLECT_VALUE_ATTRIBUTE_NAME { - attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - Ok(Self { - type_name, - generics, - attrs, - fields, - }) - } -} - /// A replacement for `#[derive(Reflect)]` to be used with foreign types which /// the definitions of cannot be altered. /// @@ -705,96 +93,14 @@ impl Parse for ReflectStructDef { /// ``` #[proc_macro] pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { - let ReflectStructDef { - type_name, - generics, - attrs, - fields, - } = parse_macro_input!(input as ReflectStructDef); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let constructor = if attrs - .data - .contains(&Ident::new("ReflectDefault", Span::call_site())) - { - Some(quote! { Default::default() }) - } else { - None + let ast = parse_macro_input!(input as DeriveInput); + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let registration_data = &attrs.data; - let get_type_registration_impl = - impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics); - - let impl_struct: proc_macro2::TokenStream = impl_struct( - &type_name, - &generics, - &get_type_registration_impl, - &bevy_reflect_path, - &attrs, - &active_fields, - ) - .into(); - - let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct( - &type_name, - &generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - constructor, - ) - .into(); + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&derive_data).into(); + let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&derive_data).into(); TokenStream::from(quote! { #impl_struct @@ -803,157 +109,6 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { }) } -#[derive(Default)] -struct ReflectAttrs { - reflect_hash: TraitImpl, - reflect_partial_eq: TraitImpl, - serialize: TraitImpl, - data: Vec, -} - -impl ReflectAttrs { - fn from_nested_metas(nested_metas: &Punctuated) -> Self { - let mut attrs = ReflectAttrs::default(); - for nested_meta in nested_metas.iter() { - match nested_meta { - NestedMeta::Lit(_) => {} - NestedMeta::Meta(meta) => match meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - let ident = segment.ident.to_string(); - match ident.as_str() { - "PartialEq" => attrs.reflect_partial_eq = TraitImpl::Implemented, - "Hash" => attrs.reflect_hash = TraitImpl::Implemented, - "Serialize" => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(Ident::new( - &format!("Reflect{}", segment.ident), - Span::call_site(), - )), - } - } - } - Meta::List(list) => { - let ident = if let Some(segment) = list.path.segments.iter().next() { - segment.ident.to_string() - } else { - continue; - }; - - if let Some(list_nested) = list.nested.iter().next() { - match list_nested { - NestedMeta::Meta(list_nested_meta) => match list_nested_meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - match ident.as_str() { - "PartialEq" => { - attrs.reflect_partial_eq = - TraitImpl::Custom(segment.ident.clone()); - } - "Hash" => { - attrs.reflect_hash = - TraitImpl::Custom(segment.ident.clone()); - } - "Serialize" => { - attrs.serialize = - TraitImpl::Custom(segment.ident.clone()); - } - _ => {} - } - } - } - Meta::List(_) => {} - Meta::NameValue(_) => {} - }, - NestedMeta::Lit(_) => {} - } - } - } - Meta::NameValue(_) => {} - }, - } - } - - attrs - } - - fn get_hash_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.reflect_hash { - TraitImpl::Implemented => quote! { - use std::hash::{Hash, Hasher}; - let mut hasher = #path::ReflectHasher::default(); - Hash::hash(&std::any::Any::type_id(self), &mut hasher); - Hash::hash(self, &mut hasher); - Some(hasher.finish()) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_partial_eq_impl(&self) -> proc_macro2::TokenStream { - match &self.reflect_partial_eq { - TraitImpl::Implemented => quote! { - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - Some(std::cmp::PartialEq::eq(self, value)) - } else { - Some(false) - } - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self, value)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_serialize_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.serialize { - TraitImpl::Implemented => quote! { - Some(#path::serde::Serializable::Borrowed(self)) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } -} - -impl Parse for ReflectAttrs { - fn parse(input: ParseStream) -> syn::Result { - let result = Punctuated::::parse_terminated(input)?; - Ok(ReflectAttrs::from_nested_metas(&result)) - } -} - -fn impl_get_type_registration( - type_name: &Ident, - bevy_reflect_path: &Path, - registration_data: &[Ident], - generics: &Generics, -) -> proc_macro2::TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { - #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { - fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { - let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* - registration - } - } - } -} - // From https://github.com/randomPoison/type-uuid #[proc_macro_derive(TypeUuid, attributes(uuid))] pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -965,117 +120,42 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { reflect_trait::reflect_trait(&args, input) } -#[proc_macro_derive(FromReflect)] +/// Derives the `FromReflect` trait. +/// +/// This macro supports the following field attributes: +/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. +/// * `#[reflect(default)]`: If the field's value cannot be read, uses its [`Default`] implementation. +/// * `#[reflect(default = "some_func")]`: If the field's value cannot be read, uses the function with the given name. +/// +#[proc_macro_derive(FromReflect, attributes(reflect))] pub fn derive_from_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - } - } - } + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct( - type_name, - &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - None, - ), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct( - type_name, + match derive_data.derive_type() { + DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), + DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), + DeriveType::Value => from_reflect::impl_value( + derive_data.type_name(), &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, + derive_data.bevy_reflect_path(), ), - DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path), } } #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let bevy_reflect_path = get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) } + +pub(crate) fn get_bevy_reflect_path() -> Path { + BevyManifest::default().get_path("bevy_reflect") +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs new file mode 100644 index 0000000000000..51297c2d72f1c --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -0,0 +1,40 @@ +use crate::container_attributes::ReflectAttrs; +use proc_macro2::Ident; +use syn::parse::{Parse, ParseStream}; +use syn::token::{Paren, Where}; +use syn::{parenthesized, Generics}; + +pub struct ReflectValueDef { + pub type_name: Ident, + pub generics: Generics, + pub attrs: Option, +} + +impl Parse for ReflectValueDef { + fn parse(input: ParseStream) -> syn::Result { + let type_ident = input.parse::()?; + let generics = input.parse::()?; + let mut lookahead = input.lookahead1(); + let mut where_clause = None; + if lookahead.peek(Where) { + where_clause = Some(input.parse()?); + lookahead = input.lookahead1(); + } + + let mut attrs = None; + if lookahead.peek(Paren) { + let content; + parenthesized!(content in input); + attrs = Some(content.parse::()?); + } + + Ok(ReflectValueDef { + type_name: type_ident, + generics: Generics { + where_clause, + ..generics + }, + attrs, + }) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs new file mode 100644 index 0000000000000..e023ce22d4f07 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -0,0 +1,22 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +pub(crate) fn impl_get_type_registration( + type_name: &Ident, + bevy_reflect_path: &Path, + registration_data: &[Ident], + generics: &Generics, +) -> proc_macro2::TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { + #[allow(unused_mut)] + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { + fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { + let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); + #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* + registration + } + } + } +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d8f7b42d8e80a..90def413de98b 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -85,7 +85,6 @@ mod tests { #[cfg(feature = "glam")] use ::glam::{vec3, Vec3}; use ::serde::de::DeserializeSeed; - use ::serde::Serialize; use bevy_utils::HashMap; use ron::{ ser::{to_string_pretty, PrettyConfig}, @@ -478,6 +477,7 @@ mod tests { #[cfg(feature = "glam")] mod glam { use super::*; + use ::serde::Serialize; #[test] fn vec3_serialization() { From 4c3f1d6767e40aafd014616a132eb94fb9c804f9 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 May 2022 23:51:56 -0700 Subject: [PATCH 02/20] Add utility module --- .../bevy_reflect_derive/src/derive_data.rs | 2 +- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 12 ++++-------- .../bevy_reflect/bevy_reflect_derive/src/utility.rs | 6 ++++++ 3 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/utility.rs 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 f65be24c86158..0a78f8e3a2851 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -2,7 +2,7 @@ use crate::container_attributes::ReflectAttrs; use crate::field_attributes::{ parse_field_attrs, ReflectFieldAttr, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, }; -use crate::get_bevy_reflect_path; +use crate::utility::get_bevy_reflect_path; use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; pub enum DeriveType { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 3de1dccb7ea47..aeecf06a8938a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -9,14 +9,14 @@ mod reflect_trait; mod reflect_value; mod registration; mod type_uuid; +mod utility; use crate::derive_data::ReflectDeriveData; -use bevy_macro_utils::BevyManifest; use derive_data::DeriveType; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; -use syn::{parse_macro_input, DeriveInput, Path}; +use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { @@ -44,7 +44,7 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = get_bevy_reflect_path(); + let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); let registration_data = &reflect_attrs.data(); @@ -151,11 +151,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream { pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = get_bevy_reflect_path(); + let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) } - -pub(crate) fn get_bevy_reflect_path() -> Path { - BevyManifest::default().get_path("bevy_reflect") -} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs new file mode 100644 index 0000000000000..edd3e901f1b1b --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -0,0 +1,6 @@ +use bevy_macro_utils::BevyManifest; +use syn::Path; + +pub fn get_bevy_reflect_path() -> Path { + BevyManifest::default().get_path("bevy_reflect") +} From bd575434ea8a493a02f6195e5470704033ba801b Mon Sep 17 00:00:00 2001 From: MrGVSV <49806985+MrGVSV@users.noreply.github.com> Date: Tue, 10 May 2022 08:51:25 -0700 Subject: [PATCH 03/20] Apply suggestions from code review Co-authored-by: Nicola Papale --- .../bevy_reflect_derive/src/derive_data.rs | 25 ++++++++--------- .../src/field_attributes.rs | 28 +++++++++---------- 2 files changed, 24 insertions(+), 29 deletions(-) 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 0a78f8e3a2851..5f6a80be2df71 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -105,25 +105,22 @@ impl<'a> ReflectDeriveData<'a> { .iter() .enumerate() .map(|(i, f)| { - ( - f, - parse_field_attrs(&f.attrs).unwrap_or_else(|err| { - if let Some(ref mut error) = errors { - error.combine(err); - } else { - errors = Some(err); - } - ReflectFieldAttr::default() - }), - i, - ) + let attr = parse_field_attrs(&f.attrs).unwrap_or_else(|err| { + if let Some(ref mut errors) = errors { + errors.combine(err); + } else { + errors = Some(err); + } + ReflectFieldAttr::default() + }); + (f, attr, i) }) .collect::>(); - - if let Some(errs) = errors { + if let Some(errs) = errors{ return Err(errs); } + Ok(output) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs index a4a589d44d620..baf82e7802619 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -41,34 +41,32 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result Result<(), syn::Error> { match meta { - Meta::Path(path) => { - if path.is_ident(IGNORE) { - args.ignore = Some(true); - } else { - return Err(syn::Error::new( - path.span(), - format!("unknown attribute parameter: {}", path.to_token_stream()), - )); - } + Meta::Path(path) if path.is_ident(IGNORE) => { + args.ignore = Some(true); + Ok(()) } + Meta::Path(path) => Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )), Meta::NameValue(pair) => { let path = &pair.path; - return Err(syn::Error::new( + Err(syn::Error::new( path.span(), format!("unknown attribute parameter: {}", path.to_token_stream()), - )); + )) + } + Meta::List(list) if !list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => { + Err(syn::Error::new(list.path.span(), "unexpected property")) } Meta::List(list) => { - if !list.path.is_ident(REFLECT_ATTRIBUTE_NAME) { - return Err(syn::Error::new(list.path.span(), "unexpected property")); - } for nested in list.nested.iter() { if let NestedMeta::Meta(meta) = nested { parse_meta(args, meta)?; } } + Ok(()) } } - Ok(()) } From c5131575c99397b09c068cc7f11934f791a9fbca Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 09:04:28 -0700 Subject: [PATCH 04/20] Add StructField type --- .../bevy_reflect_derive/src/derive_data.rs | 40 +++++++++++++------ .../bevy_reflect_derive/src/from_reflect.rs | 29 ++++++++------ .../bevy_reflect_derive/src/impls.rs | 12 +++--- 3 files changed, 50 insertions(+), 31 deletions(-) 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 5f6a80be2df71..ac533099c9cbf 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -12,6 +12,16 @@ pub enum DeriveType { Value, } +/// Represents a field on a struct or tuple struct. +pub struct StructField<'a> { + /// The raw field. + pub data: &'a Field, + /// The reflection-based attributes on the field. + pub attrs: ReflectFieldAttr, + /// The index of this field within the struct. + pub index: usize, +} + /// Data used by derive macros for `Reflect` and `FromReflect` /// /// # Example @@ -32,7 +42,7 @@ pub struct ReflectDeriveData<'a> { attrs: ReflectAttrs, type_name: &'a Ident, generics: &'a Generics, - fields: Vec<(&'a Field, ReflectFieldAttr, usize)>, + fields: Vec>, bevy_reflect_path: Path, } @@ -104,8 +114,8 @@ impl<'a> ReflectDeriveData<'a> { output.fields = fields .iter() .enumerate() - .map(|(i, f)| { - let attr = parse_field_attrs(&f.attrs).unwrap_or_else(|err| { + .map(|(index, field)| { + let attrs = parse_field_attrs(&field.attrs).unwrap_or_else(|err| { if let Some(ref mut errors) = errors { errors.combine(err); } else { @@ -113,35 +123,39 @@ impl<'a> ReflectDeriveData<'a> { } ReflectFieldAttr::default() }); - (f, attr, i) + + StructField { + index, + attrs, + data: field, + } }) - .collect::>(); - if let Some(errs) = errors{ + .collect::>(); + if let Some(errs) = errors { return Err(errs); } - Ok(output) } /// Get an iterator over the active fields - pub fn active_fields(&self) -> impl Iterator { + pub fn active_fields(&self) -> impl Iterator> { self.fields .iter() - .filter(|(_field, attrs, _i)| !attrs.ignore.unwrap_or(false)) + .filter(|field| !field.attrs.ignore.unwrap_or(false)) } /// Get an iterator over the ignored fields - pub fn ignored_fields(&self) -> impl Iterator { + pub fn ignored_fields(&self) -> impl Iterator> { self.fields .iter() - .filter(|(_field, attrs, _i)| attrs.ignore.unwrap_or(false)) + .filter(|field| field.attrs.ignore.unwrap_or(false)) } /// Get a collection of all active types pub fn active_types(&self) -> Vec { self.active_fields() - .map(|(field, ..)| field.ty.clone()) + .map(|field| field.data.ty.clone()) .collect::>() } @@ -167,7 +181,7 @@ impl<'a> ReflectDeriveData<'a> { /// The complete set of fields in this struct. #[allow(dead_code)] - pub fn fields(&self) -> &Vec<(&'a Field, ReflectFieldAttr, usize)> { + pub fn fields(&self) -> &[StructField<'a>] { &self.fields } 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 2afc47aefd713..a99f111585815 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -28,9 +28,9 @@ pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Pa /// Container for a struct's members (field name or index) and their /// corresponding values. -struct StructFields(Vec, Vec); +struct MemberValuePair(Vec, Vec); -impl StructFields { +impl MemberValuePair { pub fn new(items: (Vec, Vec)) -> Self { Self(items.0, items.1) } @@ -49,8 +49,9 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke }; let field_types = derive_data.active_types(); - let StructFields(ignored_members, ignored_values) = get_ignored_fields(derive_data, is_tuple); - let StructFields(active_members, active_values) = + let MemberValuePair(ignored_members, ignored_values) = + get_ignored_fields(derive_data, is_tuple); + let MemberValuePair(active_members, active_values) = get_active_fields(derive_data, &ref_struct, is_tuple); let default_ident = Ident::new("ReflectDefault", Span::call_site()); @@ -105,12 +106,12 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke /// Get the collection of ignored field definitions /// /// Each item in the collection takes the form: `field_ident: field_value`. -fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> StructFields { - StructFields::new( +fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { + MemberValuePair::new( derive_data .ignored_fields() - .map(|(field, _attr, index)| { - let member = get_ident(field, *index, is_tuple); + .map(|field| { + let member = get_ident(field.data, field.index, is_tuple); let value = quote! { Default::default() }; @@ -128,15 +129,16 @@ fn get_active_fields( derive_data: &ReflectDeriveData, dyn_struct_name: &Ident, is_tuple: bool, -) -> StructFields { +) -> MemberValuePair { let bevy_reflect_path = derive_data.bevy_reflect_path(); - StructFields::new( + MemberValuePair::new( derive_data .active_fields() - .map(|(field, _attr, index)| { - let member = get_ident(field, *index, is_tuple); - let ty = field.ty.clone(); + .map(|field| { + let index = field.index; + let member = get_ident(field.data, index, is_tuple); + let ty = field.data.ty.clone(); // Accesses the field on the given dynamic struct or tuple struct let get_field = if is_tuple { @@ -145,6 +147,7 @@ fn get_active_fields( } } else { let name = field + .data .ident .as_ref() .map(|i| i.to_string()) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index 50e5f320c7de9..12f107f8c9ab3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -11,22 +11,24 @@ pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { let field_names = derive_data .active_fields() - .map(|(field, _attr, index)| { + .map(|field| { field + .data .ident .as_ref() .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) + .unwrap_or_else(|| field.index.to_string()) }) .collect::>(); let field_idents = derive_data .active_fields() - .map(|(field, _attr, index)| { + .map(|field| { field + .data .ident .as_ref() .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) + .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) }) .collect::>(); let field_count = field_idents.len(); @@ -178,7 +180,7 @@ pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { let field_idents = derive_data .active_fields() - .map(|(_field, _attr, index)| Member::Unnamed(Index::from(*index))) + .map(|field| Member::Unnamed(Index::from(field.index))) .collect::>(); let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); From e7604cf37685be15bb94b7ea57afca736c68edc8 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 09:06:41 -0700 Subject: [PATCH 05/20] Replace ignore's Option with plain bool --- .../bevy_reflect/bevy_reflect_derive/src/derive_data.rs | 8 ++------ .../bevy_reflect_derive/src/field_attributes.rs | 5 ++--- 2 files changed, 4 insertions(+), 9 deletions(-) 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 ac533099c9cbf..fca7e0043f905 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -140,16 +140,12 @@ impl<'a> ReflectDeriveData<'a> { /// Get an iterator over the active fields pub fn active_fields(&self) -> impl Iterator> { - self.fields - .iter() - .filter(|field| !field.attrs.ignore.unwrap_or(false)) + self.fields.iter().filter(|field| !field.attrs.ignore) } /// Get an iterator over the ignored fields pub fn ignored_fields(&self) -> impl Iterator> { - self.fields - .iter() - .filter(|field| field.attrs.ignore.unwrap_or(false)) + self.fields.iter().filter(|field| field.attrs.ignore) } /// Get a collection of all active types diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs index baf82e7802619..12f88dede1bc4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -10,7 +10,7 @@ pub(crate) static IGNORE: &str = "ignore"; #[derive(Default)] pub struct ReflectFieldAttr { /// Determines if this field should be ignored. - pub ignore: Option, + pub ignore: bool, } /// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`). @@ -42,7 +42,7 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result Result<(), syn::Error> { match meta { Meta::Path(path) if path.is_ident(IGNORE) => { - args.ignore = Some(true); + args.ignore = true; Ok(()) } Meta::Path(path) => Err(syn::Error::new( @@ -68,5 +68,4 @@ fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error Ok(()) } } - } From 226405fc233a8f4cb97e1edb1fd974b433285419 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 20:54:05 -0700 Subject: [PATCH 06/20] Add get_reflect_ident utility Also made certain attributes into constants rather than hardcoded strings. --- .../src/container_attributes.rs | 28 +++++++++++-------- .../bevy_reflect_derive/src/derive_data.rs | 3 +- .../src/field_attributes.rs | 3 +- .../bevy_reflect_derive/src/lib.rs | 3 ++ .../bevy_reflect_derive/src/reflect_trait.rs | 6 ++-- .../bevy_reflect_derive/src/utility.rs | 15 ++++++++++ 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 2d3e6fbfd347d..1ec3f5acac6b9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -1,10 +1,17 @@ -use proc_macro2::{Ident, Span}; +use crate::utility; +use proc_macro2::Ident; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Meta, NestedMeta, Path}; +// Trait idents that are used internally for reflection. +// Received via `#[reflect(PartialEq, Hash, ...)]` +const PARTIAL_EQ_ATTR: &str = "PartialEq"; +const HASH_ATTR: &str = "Hash"; +const SERIALIZE_ATTR: &str = "Serialize"; + #[derive(Clone)] pub enum TraitImpl { NotImplemented, @@ -37,13 +44,12 @@ impl ReflectAttrs { if let Some(segment) = path.segments.iter().next() { let ident = segment.ident.to_string(); match ident.as_str() { - "PartialEq" => attrs.reflect_partial_eq = TraitImpl::Implemented, - "Hash" => attrs.reflect_hash = TraitImpl::Implemented, - "Serialize" => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(Ident::new( - &format!("Reflect{}", segment.ident), - Span::call_site(), - )), + PARTIAL_EQ_ATTR => { + attrs.reflect_partial_eq = TraitImpl::Implemented + } + HASH_ATTR => attrs.reflect_hash = TraitImpl::Implemented, + SERIALIZE_ATTR => attrs.serialize = TraitImpl::Implemented, + _ => attrs.data.push(utility::get_reflect_ident(&ident)), } } } @@ -60,15 +66,15 @@ impl ReflectAttrs { Meta::Path(path) => { if let Some(segment) = path.segments.iter().next() { match ident.as_str() { - "PartialEq" => { + PARTIAL_EQ_ATTR => { attrs.reflect_partial_eq = TraitImpl::Custom(segment.ident.clone()); } - "Hash" => { + HASH_ATTR => { attrs.reflect_hash = TraitImpl::Custom(segment.ident.clone()); } - "Serialize" => { + SERIALIZE_ATTR => { attrs.serialize = TraitImpl::Custom(segment.ident.clone()); } 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 fca7e0043f905..c37bffd0acf4d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,9 +1,10 @@ use crate::container_attributes::ReflectAttrs; use crate::field_attributes::{ - parse_field_attrs, ReflectFieldAttr, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, + parse_field_attrs, ReflectFieldAttr, }; use crate::utility::get_bevy_reflect_path; use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; +use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; pub enum DeriveType { Struct, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs index 12f88dede1bc4..63aca01a6d8f7 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -1,9 +1,8 @@ +use crate::REFLECT_ATTRIBUTE_NAME; use quote::ToTokens; use syn::spanned::Spanned; use syn::{Attribute, Meta, NestedMeta}; -pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; -pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; pub(crate) static IGNORE: &str = "ignore"; /// A container for reflection field configuration. diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index aeecf06a8938a..74f8d8dd99549 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -18,6 +18,9 @@ use quote::quote; use reflect_value::ReflectValueDef; use syn::{parse_macro_input, DeriveInput}; +pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; +pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; + #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs index c3632c2d3ef49..08607f4de6667 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs @@ -1,8 +1,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{parse::Parse, parse_macro_input, Attribute, Ident, ItemTrait, Token}; +use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token}; pub struct TraitInfo { item_trait: ItemTrait, @@ -27,8 +26,7 @@ pub fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { let item_trait = &trait_info.item_trait; let trait_ident = &item_trait.ident; let trait_vis = &item_trait.vis; - let reflect_trait_ident = - Ident::new(&format!("Reflect{}", item_trait.ident), Span::call_site()); + let reflect_trait_ident = crate::utility::get_reflect_ident(&item_trait.ident.to_string()); let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); TokenStream::from(quote! { #item_trait diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index edd3e901f1b1b..9d14b327f0328 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -1,6 +1,21 @@ +use proc_macro2::{Ident, Span}; use bevy_macro_utils::BevyManifest; use syn::Path; pub fn get_bevy_reflect_path() -> Path { BevyManifest::default().get_path("bevy_reflect") } + + +/// Returns the "reflected" ident for a given string. +/// +/// # Example +/// +/// ```ignore +/// let reflected: Ident = get_reflect_ident("Hash"); +/// assert_eq!("ReflectHash", reflected.to_string()); +/// ``` +pub fn get_reflect_ident(name: &str) -> Ident { + let reflected = format!("Reflect{}", name); + Ident::new(&reflected, Span::call_site()) +} \ No newline at end of file From 9708d365afa40e367d784f418d1029786014aac3 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 21:01:58 -0700 Subject: [PATCH 07/20] Reduce nesting --- .../src/container_attributes.rs | 81 +++++++++---------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 1ec3f5acac6b9..8ec7171972fcd 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -38,59 +38,50 @@ impl ReflectAttrs { let mut attrs = ReflectAttrs::default(); for nested_meta in nested_metas.iter() { match nested_meta { - NestedMeta::Lit(_) => {} - NestedMeta::Meta(meta) => match meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - let ident = segment.ident.to_string(); - match ident.as_str() { - PARTIAL_EQ_ATTR => { - attrs.reflect_partial_eq = TraitImpl::Implemented - } - HASH_ATTR => attrs.reflect_hash = TraitImpl::Implemented, - SERIALIZE_ATTR => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(utility::get_reflect_ident(&ident)), - } + NestedMeta::Meta(Meta::Path(path)) => { + if let Some(segment) = path.segments.iter().next() { + let ident = segment.ident.to_string(); + match ident.as_str() { + PARTIAL_EQ_ATTR => attrs.reflect_partial_eq = TraitImpl::Implemented, + HASH_ATTR => attrs.reflect_hash = TraitImpl::Implemented, + SERIALIZE_ATTR => attrs.serialize = TraitImpl::Implemented, + _ => attrs.data.push(utility::get_reflect_ident(&ident)), } } - Meta::List(list) => { - let ident = if let Some(segment) = list.path.segments.iter().next() { - segment.ident.to_string() - } else { - continue; - }; + } + NestedMeta::Meta(Meta::List(list)) => { + let ident = if let Some(segment) = list.path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; - if let Some(list_nested) = list.nested.iter().next() { - match list_nested { - NestedMeta::Meta(list_nested_meta) => match list_nested_meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - match ident.as_str() { - PARTIAL_EQ_ATTR => { - attrs.reflect_partial_eq = - TraitImpl::Custom(segment.ident.clone()); - } - HASH_ATTR => { - attrs.reflect_hash = - TraitImpl::Custom(segment.ident.clone()); - } - SERIALIZE_ATTR => { - attrs.serialize = - TraitImpl::Custom(segment.ident.clone()); - } - _ => {} - } + if let Some(list_nested) = list.nested.iter().next() { + match list_nested { + NestedMeta::Meta(Meta::Path(path)) => { + if let Some(segment) = path.segments.iter().next() { + match ident.as_str() { + PARTIAL_EQ_ATTR => { + attrs.reflect_partial_eq = + TraitImpl::Custom(segment.ident.clone()); + } + HASH_ATTR => { + attrs.reflect_hash = + TraitImpl::Custom(segment.ident.clone()); } + SERIALIZE_ATTR => { + attrs.serialize = + TraitImpl::Custom(segment.ident.clone()); + } + _ => {} } - Meta::List(_) => {} - Meta::NameValue(_) => {} - }, - NestedMeta::Lit(_) => {} + } } + _ => {} } } - Meta::NameValue(_) => {} - }, + } + _ => {} } } From a15af0ea63202a1ac845444ffe1198d88f1cb4df Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 21:08:08 -0700 Subject: [PATCH 08/20] Simplified match statement --- .../src/container_attributes.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 8ec7171972fcd..b3653aa4c79f9 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -60,19 +60,11 @@ impl ReflectAttrs { match list_nested { NestedMeta::Meta(Meta::Path(path)) => { if let Some(segment) = path.segments.iter().next() { + let trait_impl = TraitImpl::Custom(segment.ident.clone()); match ident.as_str() { - PARTIAL_EQ_ATTR => { - attrs.reflect_partial_eq = - TraitImpl::Custom(segment.ident.clone()); - } - HASH_ATTR => { - attrs.reflect_hash = - TraitImpl::Custom(segment.ident.clone()); - } - SERIALIZE_ATTR => { - attrs.serialize = - TraitImpl::Custom(segment.ident.clone()); - } + PARTIAL_EQ_ATTR => attrs.reflect_partial_eq = trait_impl, + HASH_ATTR => attrs.reflect_hash = trait_impl, + SERIALIZE_ATTR => attrs.serialize = trait_impl, _ => {} } } From f5c0e10d108592e9fb4e61b2a3dd1e92124f8ab4 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 21:12:42 -0700 Subject: [PATCH 09/20] Favor pub(crate) over pub for non-public items --- .../bevy_reflect_derive/src/container_attributes.rs | 4 ++-- .../bevy_reflect/bevy_reflect_derive/src/derive_data.rs | 6 +++--- .../bevy_reflect_derive/src/field_attributes.rs | 2 +- .../bevy_reflect/bevy_reflect_derive/src/from_reflect.rs | 6 +++--- crates/bevy_reflect/bevy_reflect_derive/src/impls.rs | 6 +++--- .../bevy_reflect_derive/src/reflect_trait.rs | 2 +- .../bevy_reflect_derive/src/reflect_value.rs | 2 +- crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs | 2 +- crates/bevy_reflect/bevy_reflect_derive/src/utility.rs | 9 ++++----- 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index b3653aa4c79f9..13bf3c26c5c3f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -13,7 +13,7 @@ const HASH_ATTR: &str = "Hash"; const SERIALIZE_ATTR: &str = "Serialize"; #[derive(Clone)] -pub enum TraitImpl { +pub(crate) enum TraitImpl { NotImplemented, Implemented, Custom(Ident), @@ -26,7 +26,7 @@ impl Default for TraitImpl { } #[derive(Default)] -pub struct ReflectAttrs { +pub(crate) struct ReflectAttrs { reflect_hash: TraitImpl, pub(crate) reflect_partial_eq: TraitImpl, serialize: TraitImpl, 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 c37bffd0acf4d..8118f94eeec59 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -6,7 +6,7 @@ use crate::utility::get_bevy_reflect_path; use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; -pub enum DeriveType { +pub(crate) enum DeriveType { Struct, TupleStruct, UnitStruct, @@ -14,7 +14,7 @@ pub enum DeriveType { } /// Represents a field on a struct or tuple struct. -pub struct StructField<'a> { +pub(crate) struct StructField<'a> { /// The raw field. pub data: &'a Field, /// The reflection-based attributes on the field. @@ -38,7 +38,7 @@ pub struct StructField<'a> { /// z: T3 // | /// } /// ``` -pub struct ReflectDeriveData<'a> { +pub(crate) struct ReflectDeriveData<'a> { derive_type: DeriveType, attrs: ReflectAttrs, type_name: &'a Ident, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs index 63aca01a6d8f7..682871dca8755 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -7,7 +7,7 @@ pub(crate) static IGNORE: &str = "ignore"; /// A container for reflection field configuration. #[derive(Default)] -pub struct ReflectFieldAttr { +pub(crate) struct ReflectFieldAttr { /// Determines if this field should be ignored. pub ignore: bool, } 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 a99f111585815..d709f473a8bda 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -5,17 +5,17 @@ use quote::quote; use syn::{Field, Generics, Ident, Index, Member, Path}; /// Implements `FromReflect` for the given struct -pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { +pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { impl_struct_internal(derive_data, false) } /// Implements `FromReflect` for the given tuple struct -pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { +pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { impl_struct_internal(derive_data, true) } /// Implements `FromReflect` for the given value type -pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { +pub(crate) fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index 12f107f8c9ab3..f9eb955c70414 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -5,7 +5,7 @@ use proc_macro2::Ident; use quote::quote; use syn::{Generics, Index, Member, Path}; -pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { +pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { let bevy_reflect_path = derive_data.bevy_reflect_path(); let struct_name = derive_data.type_name(); @@ -173,7 +173,7 @@ pub fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { }) } -pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { +pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { let bevy_reflect_path = derive_data.bevy_reflect_path(); let struct_name = derive_data.type_name(); let get_type_registration_impl = derive_data.get_type_registration(); @@ -302,7 +302,7 @@ pub fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { }) } -pub fn impl_value( +pub(crate) fn impl_value( type_name: &Ident, generics: &Generics, get_type_registration_impl: proc_macro2::TokenStream, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs index 08607f4de6667..afeb92d78228d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs @@ -3,7 +3,7 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token}; -pub struct TraitInfo { +pub(crate) struct TraitInfo { item_trait: ItemTrait, } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index 51297c2d72f1c..e337cfe95046f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -4,7 +4,7 @@ use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; use syn::{parenthesized, Generics}; -pub struct ReflectValueDef { +pub(crate) struct ReflectValueDef { pub type_name: Ident, pub generics: Generics, pub attrs: Option, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs index 382036a9f3713..8adb2dbcdad7e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs @@ -5,7 +5,7 @@ use quote::quote; use syn::*; use uuid::Uuid; -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate let mut ast: DeriveInput = syn::parse(input).unwrap(); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 9d14b327f0328..660dc91aa806b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -1,12 +1,11 @@ -use proc_macro2::{Ident, Span}; use bevy_macro_utils::BevyManifest; +use proc_macro2::{Ident, Span}; use syn::Path; -pub fn get_bevy_reflect_path() -> Path { +pub(crate) fn get_bevy_reflect_path() -> Path { BevyManifest::default().get_path("bevy_reflect") } - /// Returns the "reflected" ident for a given string. /// /// # Example @@ -15,7 +14,7 @@ pub fn get_bevy_reflect_path() -> Path { /// let reflected: Ident = get_reflect_ident("Hash"); /// assert_eq!("ReflectHash", reflected.to_string()); /// ``` -pub fn get_reflect_ident(name: &str) -> Ident { +pub(crate) fn get_reflect_ident(name: &str) -> Ident { let reflected = format!("Reflect{}", name); Ident::new(&reflected, Span::call_site()) -} \ No newline at end of file +} From f5d651cfa28aad47e5c21a061fa23ebde32e1984 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 22:31:03 -0700 Subject: [PATCH 10/20] Cleanup and documentation --- .../src/container_attributes.rs | 210 +++++++++++++----- .../bevy_reflect_derive/src/derive_data.rs | 24 +- .../bevy_reflect_derive/src/from_reflect.rs | 9 +- .../bevy_reflect_derive/src/impls.rs | 68 ++++-- .../bevy_reflect_derive/src/lib.rs | 4 +- .../bevy_reflect_derive/src/reflect_value.rs | 6 +- 6 files changed, 218 insertions(+), 103 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 13bf3c26c5c3f..7c4bbd5ef9f42 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -1,3 +1,10 @@ +//! Contains code related to container attributes for reflected types. +//! +//! A container attribute is an attribute which applies to an entire struct or enum +//! as opposed to a particular field or variant. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: +//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`. + use crate::utility; use proc_macro2::Ident; use quote::quote; @@ -6,16 +13,22 @@ use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{Meta, NestedMeta, Path}; -// Trait idents that are used internally for reflection. -// Received via `#[reflect(PartialEq, Hash, ...)]` +// The "special" trait idents that are used internally for reflection. +// Received via attributes like `#[reflect(PartialEq, Hash, ...)]` const PARTIAL_EQ_ATTR: &str = "PartialEq"; const HASH_ATTR: &str = "Hash"; const SERIALIZE_ATTR: &str = "Serialize"; +/// A marker for trait implementations registered via the `Reflect` derive macro. #[derive(Clone)] pub(crate) enum TraitImpl { + /// The trait is not registered as implemented. NotImplemented, + /// The trait is registered as implemented. Implemented, + + // TODO: This can be made to use `ExprPath` instead of `Ident`, allowing for fully qualified paths to be used + /// The trait is registered with a custom function rather than an actual implementation. Custom(Ident), } @@ -25,120 +38,199 @@ impl Default for TraitImpl { } } +/// A collection of traits that have been registered for a reflected type. +/// +/// This keeps track of a few traits that are utilized internally for reflection +/// (we'll call these traits _special traits_ within this context), but it +/// will also keep track of all registered traits. Traits are registered as part of the +/// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`. +/// +/// The list of special traits are as follows: +/// * `Hash` +/// * `PartialEq` +/// * `Serialize` +/// +/// When registering a trait, there are a few things to keep in mind: +/// * Traits must have a valid `Reflect{}` struct in scope. For example, `Default` +/// needs `bevy_reflect::prelude::ReflectDefault` in scope. +/// * Traits must be single path identifiers. This means you _must_ use `Default` +/// instead of `std::default::Default` (otherwise it will try to register `Reflectstd`!) +/// * A custom function may be supplied in place of an actual implementation +/// for the special traits (but still follows the same single-path identifier +/// rules as normal). +/// +/// # Example +/// +/// Registering the `Default` implementation: +/// +/// ```ignore +/// // Import ReflectDefault so it's accessible by the derive macro +/// use bevy_reflect::prelude::ReflectDefault. +/// +/// #[derive(Reflect, Default)] +/// #[reflect(Default)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation: +/// +/// ```ignore +/// // `Hash` is a "special trait" and does not need (nor have) a ReflectHash struct +/// +/// #[derive(Reflect, Hash)] +/// #[reflect(Hash)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation using a custom function: +/// +/// ```ignore +/// // This function acts as our `Hash` implementation and +/// // corresponds to the `Reflect::reflect_hash` method. +/// fn get_hash(foo: &Foo) -> Option { +/// Some(123) +/// } +/// +/// #[derive(Reflect)] +/// // Register the custom `Hash` function +/// #[reflect(Hash(get_hash))] +/// struct Foo; +/// ``` +/// +/// > __Note:__ Registering a custom function only works for special traits. +/// #[derive(Default)] -pub(crate) struct ReflectAttrs { - reflect_hash: TraitImpl, - pub(crate) reflect_partial_eq: TraitImpl, +pub(crate) struct ReflectTraits { + hash: TraitImpl, + partial_eq: TraitImpl, serialize: TraitImpl, - data: Vec, + idents: Vec, } -impl ReflectAttrs { +impl ReflectTraits { + /// Create a new [`ReflectTraits`] instance from a set of nested metas. pub fn from_nested_metas(nested_metas: &Punctuated) -> Self { - let mut attrs = ReflectAttrs::default(); + let mut traits = ReflectTraits::default(); for nested_meta in nested_metas.iter() { match nested_meta { + // Handles `#[reflect( Hash, Default, ... )]` NestedMeta::Meta(Meta::Path(path)) => { - if let Some(segment) = path.segments.iter().next() { - let ident = segment.ident.to_string(); - match ident.as_str() { - PARTIAL_EQ_ATTR => attrs.reflect_partial_eq = TraitImpl::Implemented, - HASH_ATTR => attrs.reflect_hash = TraitImpl::Implemented, - SERIALIZE_ATTR => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(utility::get_reflect_ident(&ident)), - } + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) + let ident = if let Some(segment) = path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = TraitImpl::Implemented, + HASH_ATTR => traits.hash = TraitImpl::Implemented, + SERIALIZE_ATTR => traits.serialize = TraitImpl::Implemented, + // We only track reflected idents for traits not considered special + _ => traits.idents.push(utility::get_reflect_ident(&ident)), } } + // Handles `#[reflect( Hash(custom_hash_fn) )]` NestedMeta::Meta(Meta::List(list)) => { + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) let ident = if let Some(segment) = list.path.segments.iter().next() { segment.ident.to_string() } else { continue; }; - if let Some(list_nested) = list.nested.iter().next() { - match list_nested { - NestedMeta::Meta(Meta::Path(path)) => { - if let Some(segment) = path.segments.iter().next() { - let trait_impl = TraitImpl::Custom(segment.ident.clone()); - match ident.as_str() { - PARTIAL_EQ_ATTR => attrs.reflect_partial_eq = trait_impl, - HASH_ATTR => attrs.reflect_hash = trait_impl, - SERIALIZE_ATTR => attrs.serialize = trait_impl, - _ => {} - } + match list.nested.iter().next() { + Some(NestedMeta::Meta(Meta::Path(path))) => { + if let Some(segment) = path.segments.iter().next() { + // This should be the ident of the custom function + let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, + HASH_ATTR => traits.hash = trait_func_ident, + SERIALIZE_ATTR => traits.serialize = trait_func_ident, + _ => {} } } - _ => {} } + _ => {} } } _ => {} } } - attrs + traits + } + + /// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`) + /// is registered for this type. + pub fn contains(&self, name: &str) -> bool { + self.idents.iter().any(|ident| ident == name) } - pub fn data(&self) -> &[Ident] { - &self.data + /// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`). + pub fn idents(&self) -> &[Ident] { + &self.idents } - pub fn get_hash_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.reflect_hash { - TraitImpl::Implemented => quote! { + /// Returns the logic for `Reflect::reflect_hash` as a `TokenStream`. + /// + /// If `Hash` was not registered, returns `None`. + pub fn get_hash_impl(&self, path: &Path) -> Option { + match &self.hash { + TraitImpl::Implemented => Some(quote! { use std::hash::{Hash, Hasher}; let mut hasher = #path::ReflectHasher::default(); Hash::hash(&std::any::Any::type_id(self), &mut hasher); Hash::hash(self, &mut hasher); Some(hasher.finish()) - }, - TraitImpl::Custom(impl_fn) => quote! { + }), + TraitImpl::Custom(impl_fn) => Some(quote! { Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, + }), + TraitImpl::NotImplemented => None, } } - pub fn get_partial_eq_impl(&self) -> proc_macro2::TokenStream { - match &self.reflect_partial_eq { - TraitImpl::Implemented => quote! { + /// Returns the logic for `Reflect::reflect_partial_eq` as a `TokenStream`. + /// + /// If `PartialEq` was not registered, returns `None`. + pub fn get_partial_eq_impl(&self) -> Option { + match &self.partial_eq { + TraitImpl::Implemented => Some(quote! { let value = value.any(); if let Some(value) = value.downcast_ref::() { Some(std::cmp::PartialEq::eq(self, value)) } else { Some(false) } - }, - TraitImpl::Custom(impl_fn) => quote! { + }), + TraitImpl::Custom(impl_fn) => Some(quote! { Some(#impl_fn(self, value)) - }, - TraitImpl::NotImplemented => quote! { - None - }, + }), + TraitImpl::NotImplemented => None, } } - pub fn get_serialize_impl(&self, path: &Path) -> proc_macro2::TokenStream { + /// Returns the logic for `Reflect::serializable` as a `TokenStream`. + /// + /// If `Serialize` was not registered, returns `None`. + pub fn get_serialize_impl(&self, path: &Path) -> Option { match &self.serialize { - TraitImpl::Implemented => quote! { + TraitImpl::Implemented => Some(quote! { Some(#path::serde::Serializable::Borrowed(self)) - }, - TraitImpl::Custom(impl_fn) => quote! { + }), + TraitImpl::Custom(impl_fn) => Some(quote! { Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, + }), + TraitImpl::NotImplemented => None, } } } -impl Parse for ReflectAttrs { +impl Parse for ReflectTraits { fn parse(input: ParseStream) -> syn::Result { let result = Punctuated::::parse_terminated(input)?; - Ok(ReflectAttrs::from_nested_metas(&result)) + Ok(ReflectTraits::from_nested_metas(&result)) } } 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 8118f94eeec59..219fe8158bcac 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,10 +1,8 @@ -use crate::container_attributes::ReflectAttrs; -use crate::field_attributes::{ - parse_field_attrs, ReflectFieldAttr, -}; +use crate::container_attributes::ReflectTraits; +use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; use crate::utility::get_bevy_reflect_path; -use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; pub(crate) enum DeriveType { Struct, @@ -40,7 +38,7 @@ pub(crate) struct StructField<'a> { /// ``` pub(crate) struct ReflectDeriveData<'a> { derive_type: DeriveType, - attrs: ReflectAttrs, + traits: ReflectTraits, type_name: &'a Ident, generics: &'a Generics, fields: Vec>, @@ -54,7 +52,7 @@ impl<'a> ReflectDeriveData<'a> { derive_type: DeriveType::Value, generics: &input.generics, fields: Vec::new(), - attrs: ReflectAttrs::default(), + traits: ReflectTraits::default(), bevy_reflect_path: get_bevy_reflect_path(), }; @@ -70,10 +68,10 @@ impl<'a> ReflectDeriveData<'a> { if let Some(ident) = meta_list.path.get_ident() { if ident == REFLECT_ATTRIBUTE_NAME { - output.attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); + output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { force_reflect_value = true; - output.attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); + output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); } } } @@ -161,9 +159,9 @@ impl<'a> ReflectDeriveData<'a> { &self.derive_type } - /// The list of reflect-related attributes on this struct. - pub fn attrs(&self) -> &ReflectAttrs { - &self.attrs + /// The registered reflect traits on this struct. + pub fn traits(&self) -> &ReflectTraits { + &self.traits } /// The name of this struct. @@ -192,7 +190,7 @@ impl<'a> ReflectDeriveData<'a> { crate::registration::impl_get_type_registration( self.type_name, &self.bevy_reflect_path, - self.attrs.data(), + self.traits.idents(), self.generics, ) } 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 d709f473a8bda..ecacef9abd622 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -15,7 +15,11 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream } /// Implements `FromReflect` for the given value type -pub(crate) fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { +pub(crate) fn impl_value( + type_name: &Ident, + generics: &Generics, + bevy_reflect_path: &Path, +) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { @@ -54,8 +58,7 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke let MemberValuePair(active_members, active_values) = get_active_fields(derive_data, &ref_struct, is_tuple); - let default_ident = Ident::new("ReflectDefault", Span::call_site()); - let constructor = if derive_data.attrs().data().contains(&default_ident) { + let constructor = if derive_data.traits().contains("ReflectDefault") { quote!( let mut __this = Self::default(); #( diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index f9eb955c70414..79931bfa5ca40 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -1,4 +1,4 @@ -use crate::container_attributes::{ReflectAttrs, TraitImpl}; +use crate::container_attributes::ReflectTraits; use crate::ReflectDeriveData; use proc_macro::TokenStream; use proc_macro2::Ident; @@ -34,15 +34,23 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); - let hash_fn = derive_data.attrs().get_hash_impl(bevy_reflect_path); - let serialize_fn = derive_data.attrs().get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match derive_data.attrs().reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::Struct; - #bevy_reflect_path::struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => derive_data.attrs().get_partial_eq_impl(), - }; + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + use #bevy_reflect_path::Struct; + #bevy_reflect_path::struct_partial_eq(self, value) + } + }); let get_type_registration_impl = derive_data.get_type_registration(); let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); @@ -185,15 +193,23 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream let field_count = field_idents.len(); let field_indices = (0..field_count).collect::>(); - let hash_fn = derive_data.attrs().get_hash_impl(bevy_reflect_path); - let serialize_fn = derive_data.attrs().get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match derive_data.attrs().reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::TupleStruct; - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => derive_data.attrs().get_partial_eq_impl(), - }; + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + use #bevy_reflect_path::TupleStruct; + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + } + }); let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); TokenStream::from(quote! { @@ -307,11 +323,17 @@ pub(crate) fn impl_value( generics: &Generics, get_type_registration_impl: proc_macro2::TokenStream, bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, + reflect_attrs: &ReflectTraits, ) -> TokenStream { - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_attrs.get_partial_eq_impl(); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); + let hash_fn = reflect_attrs + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = reflect_attrs + .get_partial_eq_impl() + .unwrap_or_else(|| quote!(None)); + let serialize_fn = reflect_attrs + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); TokenStream::from(quote! { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 74f8d8dd99549..b825c5757c2e1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -38,7 +38,7 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { derive_data.generics(), derive_data.get_type_registration(), derive_data.bevy_reflect_path(), - derive_data.attrs(), + derive_data.traits(), ), } } @@ -50,7 +50,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); - let registration_data = &reflect_attrs.data(); + let registration_data = &reflect_attrs.idents(); let get_type_registration_impl = registration::impl_get_type_registration( ty, &bevy_reflect_path, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index e337cfe95046f..b70168b67e4e6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,4 +1,4 @@ -use crate::container_attributes::ReflectAttrs; +use crate::container_attributes::ReflectTraits; use proc_macro2::Ident; use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; @@ -7,7 +7,7 @@ use syn::{parenthesized, Generics}; pub(crate) struct ReflectValueDef { pub type_name: Ident, pub generics: Generics, - pub attrs: Option, + pub attrs: Option, } impl Parse for ReflectValueDef { @@ -25,7 +25,7 @@ impl Parse for ReflectValueDef { if lookahead.peek(Paren) { let content; parenthesized!(content in input); - attrs = Some(content.parse::()?); + attrs = Some(content.parse::()?); } Ok(ReflectValueDef { From 768750bb1264b7956259cf2b764345053732e9d1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 22:41:07 -0700 Subject: [PATCH 11/20] Added direct path access to BevyManifest --- crates/bevy_macro_utils/src/lib.rs | 16 ++++++++++++++++ .../bevy_reflect_derive/src/utility.rs | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/bevy_macro_utils/src/lib.rs b/crates/bevy_macro_utils/src/lib.rs index 4883999039dd8..717d23ecf4233 100644 --- a/crates/bevy_macro_utils/src/lib.rs +++ b/crates/bevy_macro_utils/src/lib.rs @@ -78,6 +78,22 @@ impl BevyManifest { deps.and_then(find_in_deps) .or_else(|| deps_dev.and_then(find_in_deps)) } + + /// Returns the path for the crate with the given name. + /// + /// This is a convenience method for constructing a [manifest] and + /// calling the [`get_path`] method. + /// + /// This method should only be used where you just need the path and can't + /// cache the [manifest]. If caching is possible, it's recommended to create + /// the [manifest] yourself and use the [`get_path`] method. + /// + /// [`get_path`]: Self::get_path + /// [manifest]: Self + pub fn just_get_path(name: &str) -> syn::Path { + Self::default().get_path(name) + } + pub fn get_path(&self, name: &str) -> syn::Path { self.maybe_get_path(name) .unwrap_or_else(|| Self::parse_str(name)) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 660dc91aa806b..ed883077ac54b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,8 +2,9 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; use syn::Path; +/// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { - BevyManifest::default().get_path("bevy_reflect") + BevyManifest::just_get_path("bevy_reflect") } /// Returns the "reflected" ident for a given string. From 0c70b785d644ff156925b45038cd84b1867fc439 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 22:47:09 -0700 Subject: [PATCH 12/20] Cleanup and docs for ReflectValueDef --- .../bevy_reflect_derive/src/lib.rs | 6 ++--- .../bevy_reflect_derive/src/reflect_value.rs | 22 +++++++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index b825c5757c2e1..e8b6cf7077d54 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -49,8 +49,8 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; - let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); - let registration_data = &reflect_attrs.idents(); + let reflect_traits = reflect_value_def.traits.unwrap_or_default(); + let registration_data = &reflect_traits.idents(); let get_type_registration_impl = registration::impl_get_type_registration( ty, &bevy_reflect_path, @@ -62,7 +62,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { &reflect_value_def.generics, get_type_registration_impl, &bevy_reflect_path, - &reflect_attrs, + &reflect_traits, ) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index b70168b67e4e6..ec54b99a6f404 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -4,10 +4,24 @@ use syn::parse::{Parse, ParseStream}; use syn::token::{Paren, Where}; use syn::{parenthesized, Generics}; +/// A struct used to define a simple reflected value type (such as primitives). +/// +/// This takes the form: +/// +/// ```ignore +/// // Standard +/// foo(TraitA, TraitB) +/// +/// // With generics +/// foo(TraitA, TraitB) +/// +/// // With generics and where clause +/// foo where T1: Bar (TraitA, TraitB) +/// ``` pub(crate) struct ReflectValueDef { pub type_name: Ident, pub generics: Generics, - pub attrs: Option, + pub traits: Option, } impl Parse for ReflectValueDef { @@ -21,11 +35,11 @@ impl Parse for ReflectValueDef { lookahead = input.lookahead1(); } - let mut attrs = None; + let mut traits = None; if lookahead.peek(Paren) { let content; parenthesized!(content in input); - attrs = Some(content.parse::()?); + traits = Some(content.parse::()?); } Ok(ReflectValueDef { @@ -34,7 +48,7 @@ impl Parse for ReflectValueDef { where_clause, ..generics }, - attrs, + traits, }) } } From 1538bf96fdcd5d09d938e812cd95a6743144916f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 22:54:53 -0700 Subject: [PATCH 13/20] More cleanup --- .../bevy_reflect_derive/src/field_attributes.rs | 12 +++++++++--- crates/bevy_reflect/bevy_reflect_derive/src/impls.rs | 3 +++ .../bevy_reflect_derive/src/reflect_trait.rs | 2 +- .../bevy_reflect_derive/src/registration.rs | 3 +++ .../bevy_reflect/bevy_reflect_derive/src/utility.rs | 2 ++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs index 682871dca8755..a0f3d2b6bc3a0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -1,11 +1,17 @@ +//! Contains code related to field attributes for reflected types. +//! +//! A field attribute is an attribute which applies to particular field or variant +//! as opposed to an entire struct or enum. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`. + use crate::REFLECT_ATTRIBUTE_NAME; use quote::ToTokens; use syn::spanned::Spanned; use syn::{Attribute, Meta, NestedMeta}; -pub(crate) static IGNORE: &str = "ignore"; +pub(crate) static IGNORE_ATTR: &str = "ignore"; -/// A container for reflection field configuration. +/// A container for attributes defined on a field reflected type's field. #[derive(Default)] pub(crate) struct ReflectFieldAttr { /// Determines if this field should be ignored. @@ -40,7 +46,7 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result Result<(), syn::Error> { match meta { - Meta::Path(path) if path.is_ident(IGNORE) => { + Meta::Path(path) if path.is_ident(IGNORE_ATTR) => { args.ignore = true; Ok(()) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index 79931bfa5ca40..a2656694d1bc5 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -5,6 +5,7 @@ use proc_macro2::Ident; use quote::quote; use syn::{Generics, Index, Member, Path}; +/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { let bevy_reflect_path = derive_data.bevy_reflect_path(); let struct_name = derive_data.type_name(); @@ -181,6 +182,7 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { }) } +/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { let bevy_reflect_path = derive_data.bevy_reflect_path(); let struct_name = derive_data.type_name(); @@ -318,6 +320,7 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream }) } +/// Implements `GetTypeRegistration` and `Reflect` for the given type data. pub(crate) fn impl_value( type_name: &Ident, generics: &Generics, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs index afeb92d78228d..fe4d4a6bb257e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs @@ -21,7 +21,7 @@ impl Parse for TraitInfo { } } -pub fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { +pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { let trait_info = parse_macro_input!(input as TraitInfo); let item_trait = &trait_info.item_trait; let trait_ident = &item_trait.ident; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index e023ce22d4f07..ea3157242211e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -1,7 +1,10 @@ +//! Contains code related specifically to Bevy's type registration. + use proc_macro2::Ident; use quote::quote; use syn::{Generics, Path}; +/// Creates the `GetTypeRegistration` impl for the given type data. pub(crate) fn impl_get_type_registration( type_name: &Ident, bevy_reflect_path: &Path, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index ed883077ac54b..f42a7ccf6def1 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -1,3 +1,5 @@ +//! General-purpose utility functions for internal usage within this crate. + use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; use syn::Path; From a4d88ad4cb55506f7cd6132df33d7d5047155329 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 10 May 2022 23:01:54 -0700 Subject: [PATCH 14/20] Fix clippy errors --- .../src/container_attributes.rs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 7c4bbd5ef9f42..8e16e77626c5a 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -139,20 +139,18 @@ impl ReflectTraits { continue; }; - match list.nested.iter().next() { - Some(NestedMeta::Meta(Meta::Path(path))) => { - if let Some(segment) = path.segments.iter().next() { - // This should be the ident of the custom function - let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); - match ident.as_str() { - PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, - HASH_ATTR => traits.hash = trait_func_ident, - SERIALIZE_ATTR => traits.serialize = trait_func_ident, - _ => {} - } + let list_meta = list.nested.iter().next(); + if let Some(NestedMeta::Meta(Meta::Path(path))) = list_meta { + if let Some(segment) = path.segments.iter().next() { + // This should be the ident of the custom function + let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, + HASH_ATTR => traits.hash = trait_func_ident, + SERIALIZE_ATTR => traits.serialize = trait_func_ident, + _ => {} } } - _ => {} } } _ => {} From 86d0b429a0e8c7a8f7041f83ab52df22dad16677 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 08:30:11 -0700 Subject: [PATCH 15/20] Update to get_path_direct --- crates/bevy_macro_utils/src/lib.rs | 2 +- crates/bevy_reflect/bevy_reflect_derive/src/utility.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_macro_utils/src/lib.rs b/crates/bevy_macro_utils/src/lib.rs index 717d23ecf4233..d1ab5cc40f60f 100644 --- a/crates/bevy_macro_utils/src/lib.rs +++ b/crates/bevy_macro_utils/src/lib.rs @@ -90,7 +90,7 @@ impl BevyManifest { /// /// [`get_path`]: Self::get_path /// [manifest]: Self - pub fn just_get_path(name: &str) -> syn::Path { + pub fn get_path_direct(name: &str) -> syn::Path { Self::default().get_path(name) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index f42a7ccf6def1..34fbdf186cc94 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -6,7 +6,7 @@ use syn::Path; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { - BevyManifest::just_get_path("bevy_reflect") + BevyManifest::get_path_direct("bevy_reflect") } /// Returns the "reflected" ident for a given string. From 7b32e97fe5ea8bb109e177f55cbb0c29d32f9533 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 16:53:55 -0700 Subject: [PATCH 16/20] Add crate-level doc comment --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index e8b6cf7077d54..a6d5336be6fca 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -1,3 +1,17 @@ +//! This crate contains macros used by Bevy's `Reflect` API. +//! +//! The main export of this crate is the derive macro for [`Reflect`]. This allows +//! types to easily implement `Reflect` along with other `bevy_reflect` traits, +//! such as `Struct`, `GetTypeRegistration`, and more— all with a single derive! +//! +//! Some other noteworthy exports include the derive macros for [`FromReflect`] and +//! [`TypeUuid`], as well as the [`reflect_trait`] attribute macro. +//! +//! [`Reflect`]: crate::derive_reflect +//! [`FromReflect`]: crate::derive_from_reflect +//! [`TypeUuid`]: crate::type_uuid_derive +//! [`reflect_trait`]: crate::reflect_trait + extern crate proc_macro; mod container_attributes; From 7e2fcbec263da4278b725617c1ce2d9428eb0a57 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 17:01:45 -0700 Subject: [PATCH 17/20] Rename type_uuid_derive -> derive_type_uuid for consistency --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index a6d5336be6fca..37b4baee6fa47 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -9,7 +9,7 @@ //! //! [`Reflect`]: crate::derive_reflect //! [`FromReflect`]: crate::derive_from_reflect -//! [`TypeUuid`]: crate::type_uuid_derive +//! [`TypeUuid`]: crate::derive_type_uuid //! [`reflect_trait`]: crate::reflect_trait extern crate proc_macro; @@ -128,7 +128,7 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { // From https://github.com/randomPoison/type-uuid #[proc_macro_derive(TypeUuid, attributes(uuid))] -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn derive_type_uuid(input: proc_macro::TokenStream) -> proc_macro::TokenStream { type_uuid::type_uuid_derive(input) } From c72b56fe28b1b96996e8cd788582ffc6e04a7cfb Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 17:03:34 -0700 Subject: [PATCH 18/20] Changed macro function order Now the order is: derive macros, attribute macros, then standard proc macros. --- .../bevy_reflect_derive/src/lib.rs | 74 +++++++++---------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 37b4baee6fa47..bc16e55655371 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -57,6 +57,42 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { } } +/// Derives the `FromReflect` trait. +/// +/// This macro supports the following field attributes: +/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. +/// +#[proc_macro_derive(FromReflect, attributes(reflect))] +pub fn derive_from_reflect(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; + + match derive_data.derive_type() { + DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), + DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), + DeriveType::Value => from_reflect::impl_value( + derive_data.type_name(), + &ast.generics, + derive_data.bevy_reflect_path(), + ), + } +} + +// From https://github.com/randomPoison/type-uuid +#[proc_macro_derive(TypeUuid, attributes(uuid))] +pub fn derive_type_uuid(input: TokenStream) -> TokenStream { + type_uuid::type_uuid_derive(input) +} + +#[proc_macro_attribute] +pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { + reflect_trait::reflect_trait(&args, input) +} + #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let reflect_value_def = parse_macro_input!(input as ReflectValueDef); @@ -126,44 +162,6 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { }) } -// From https://github.com/randomPoison/type-uuid -#[proc_macro_derive(TypeUuid, attributes(uuid))] -pub fn derive_type_uuid(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - type_uuid::type_uuid_derive(input) -} - -#[proc_macro_attribute] -pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { - reflect_trait::reflect_trait(&args, input) -} - -/// Derives the `FromReflect` trait. -/// -/// This macro supports the following field attributes: -/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. -/// * `#[reflect(default)]`: If the field's value cannot be read, uses its [`Default`] implementation. -/// * `#[reflect(default = "some_func")]`: If the field's value cannot be read, uses the function with the given name. -/// -#[proc_macro_derive(FromReflect, attributes(reflect))] -pub fn derive_from_reflect(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - - let derive_data = match ReflectDeriveData::from_input(&ast) { - Ok(data) => data, - Err(err) => return err.into_compile_error().into(), - }; - - match derive_data.derive_type() { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), - DeriveType::Value => from_reflect::impl_value( - derive_data.type_name(), - &ast.generics, - derive_data.bevy_reflect_path(), - ), - } -} - #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { let reflect_value_def = parse_macro_input!(input as ReflectValueDef); From e77f20f1b99aa579e54875b170e477fe8c06d05d Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 17:23:07 -0700 Subject: [PATCH 19/20] Fix bevy_reflect_path bug Removed all usages of `use #bevy_reflect_path` --- .../bevy_reflect_derive/src/from_reflect.rs | 53 +++++++++---------- .../bevy_reflect_derive/src/impls.rs | 8 +-- 2 files changed, 28 insertions(+), 33 deletions(-) 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 ecacef9abd622..8e9e9c679295b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -2,7 +2,7 @@ use crate::ReflectDeriveData; use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{Field, Generics, Ident, Index, Member, Path}; +use syn::{Field, Generics, Ident, Index, Lit, LitInt, LitStr, Member, Path}; /// Implements `FromReflect` for the given struct pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { @@ -56,7 +56,7 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(derive_data, is_tuple); let MemberValuePair(active_members, active_values) = - get_active_fields(derive_data, &ref_struct, is_tuple); + get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple); let constructor = if derive_data.traits().contains("ReflectDefault") { quote!( @@ -95,7 +95,6 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - use #bevy_reflect_path::#ref_struct_type; if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = reflect.reflect_ref() { #constructor } else { @@ -107,8 +106,6 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke } /// Get the collection of ignored field definitions -/// -/// Each item in the collection takes the form: `field_ident: field_value`. fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { MemberValuePair::new( derive_data @@ -126,11 +123,10 @@ fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> Member } /// Get the collection of active field definitions -/// -/// Each item in the collection takes the form: `field_ident: field_value`. fn get_active_fields( derive_data: &ReflectDeriveData, dyn_struct_name: &Ident, + struct_type: &Ident, is_tuple: bool, ) -> MemberValuePair { let bevy_reflect_path = derive_data.bevy_reflect_path(); @@ -139,29 +135,15 @@ fn get_active_fields( derive_data .active_fields() .map(|field| { - let index = field.index; - let member = get_ident(field.data, index, is_tuple); + let member = get_ident(field.data, field.index, is_tuple); + let accessor = get_field_accessor(field.data, field.index, is_tuple); let ty = field.data.ty.clone(); - // Accesses the field on the given dynamic struct or tuple struct - let get_field = if is_tuple { - quote! { - #dyn_struct_name.field(#index) - } - } else { - let name = field - .data - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()); - quote! { - #dyn_struct_name.field(#name) - } - }; - let value = quote! { { - <#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)? + <#ty as #bevy_reflect_path::FromReflect>::from_reflect( + // Accesses the field on the given dynamic struct or tuple struct + #bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)? + )? }}; (member, value) @@ -170,6 +152,7 @@ fn get_active_fields( ) } +/// Returns the member for a given field of a struct or tuple struct. fn get_ident(field: &Field, index: usize, is_tuple: bool) -> Member { if is_tuple { Member::Unnamed(Index::from(index)) @@ -181,3 +164,19 @@ fn get_ident(field: &Field, index: usize, is_tuple: bool) -> Member { .unwrap_or_else(|| Member::Unnamed(Index::from(index))) } } + +/// Returns the accessor for a given field of a struct or tuple struct. +/// +/// This differs from a member in that it needs to be a number for tuple structs +/// and a string for standard structs. +fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit { + if is_tuple { + Lit::Int(LitInt::new(&index.to_string(), Span::call_site())) + } else { + field + .ident + .as_ref() + .map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site()))) + .unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site()))) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index a2656694d1bc5..18b4c0ab96e4b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -48,7 +48,6 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { .get_partial_eq_impl() .unwrap_or_else(|| { quote! { - use #bevy_reflect_path::Struct; #bevy_reflect_path::struct_partial_eq(self, value) } }); @@ -208,7 +207,6 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream .get_partial_eq_impl() .unwrap_or_else(|| { quote! { - use #bevy_reflect_path::TupleStruct; #bevy_reflect_path::tuple_struct_partial_eq(self, value) } }); @@ -276,8 +274,7 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream #[inline] fn clone_value(&self) -> Box { - use #bevy_reflect_path::TupleStruct; - Box::new(self.clone_dynamic()) + Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) } #[inline] fn set(&mut self, value: Box) -> Result<(), Box> { @@ -287,10 +284,9 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream #[inline] fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - use #bevy_reflect_path::TupleStruct; if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { for (i, value) in struct_value.iter_fields().enumerate() { - self.field_mut(i).map(|v| v.apply(value)); + #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); } } else { panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); From ca2b1372a54df847e942c24bf3271598313e3968 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 11 May 2022 17:35:20 -0700 Subject: [PATCH 20/20] Fix broken doc link --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 6 +++--- .../src/{reflect_trait.rs => trait_reflection.rs} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename crates/bevy_reflect/bevy_reflect_derive/src/{reflect_trait.rs => trait_reflection.rs} (100%) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index bc16e55655371..8d59def2c0f9b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -10,7 +10,7 @@ //! [`Reflect`]: crate::derive_reflect //! [`FromReflect`]: crate::derive_from_reflect //! [`TypeUuid`]: crate::derive_type_uuid -//! [`reflect_trait`]: crate::reflect_trait +//! [`reflect_trait`]: macro@reflect_trait extern crate proc_macro; @@ -19,9 +19,9 @@ mod derive_data; mod field_attributes; mod from_reflect; mod impls; -mod reflect_trait; mod reflect_value; mod registration; +mod trait_reflection; mod type_uuid; mod utility; @@ -90,7 +90,7 @@ pub fn derive_type_uuid(input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { - reflect_trait::reflect_trait(&args, input) + trait_reflection::reflect_trait(&args, input) } #[proc_macro] diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs similarity index 100% rename from crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs rename to crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs