mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
bevy_reflect: Fix trailing comma breaking derives (#8014)
# Objective Fixes #7989 Based on #7991 by @CoffeeVampir3 ## Solution There were three parts to this issue: 1. `extend_where_clause` did not account for the optionality of a where clause's trailing comma ```rust // OKAY struct Foo<T> where T: Asset, {/* ... */} // ERROR struct Foo<T> where T: Asset {/* ... */} ``` 2. `FromReflect` derive logic was not actively using `extend_where_clause` which led to some inconsistencies (enums weren't adding _any_ additional bounds even) 3. Using `extend_where_clause` in the `FromReflect` derive logic meant we had to optionally add `Default` bounds to ignored fields iff the entire item itself was not already `Default` (otherwise the definition for `Handle<T>` wouldn't compile since `HandleType` doesn't impl `Default` but `Handle<T>` itself does) --- ## Changelog - Fixed issue where a missing trailing comma could break the reflection derives
This commit is contained in:
parent
846b748c6f
commit
5e5a305d43
3 changed files with 314 additions and 16 deletions
|
@ -3,7 +3,7 @@ use crate::derive_data::ReflectEnum;
|
||||||
use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors};
|
use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors};
|
||||||
use crate::field_attributes::DefaultBehavior;
|
use crate::field_attributes::DefaultBehavior;
|
||||||
use crate::fq_std::{FQAny, FQClone, FQDefault, FQOption};
|
use crate::fq_std::{FQAny, FQClone, FQDefault, FQOption};
|
||||||
use crate::utility::ident_or_index;
|
use crate::utility::{extend_where_clause, ident_or_index, WhereClauseOptions};
|
||||||
use crate::{ReflectMeta, ReflectStruct};
|
use crate::{ReflectMeta, ReflectStruct};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
|
@ -49,8 +49,20 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) =
|
let (impl_generics, ty_generics, where_clause) =
|
||||||
reflect_enum.meta().generics().split_for_impl();
|
reflect_enum.meta().generics().split_for_impl();
|
||||||
|
|
||||||
|
// Add FromReflect bound for each active field
|
||||||
|
let where_from_reflect_clause = extend_where_clause(
|
||||||
|
where_clause,
|
||||||
|
&WhereClauseOptions {
|
||||||
|
active_types: reflect_enum.active_types().into_boxed_slice(),
|
||||||
|
ignored_types: reflect_enum.ignored_types().into_boxed_slice(),
|
||||||
|
active_trait_bounds: quote!(#bevy_reflect_path::FromReflect),
|
||||||
|
ignored_trait_bounds: quote!(#FQDefault),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause {
|
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_from_reflect_clause {
|
||||||
fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
|
fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
|
||||||
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) {
|
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) {
|
||||||
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
|
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
|
||||||
|
@ -89,11 +101,11 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
|
||||||
Ident::new("Struct", Span::call_site())
|
Ident::new("Struct", Span::call_site())
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_types = reflect_struct.active_types();
|
|
||||||
let MemberValuePair(active_members, active_values) =
|
let MemberValuePair(active_members, active_values) =
|
||||||
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
|
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
|
||||||
|
|
||||||
let constructor = if reflect_struct.meta().traits().contains(REFLECT_DEFAULT) {
|
let is_defaultable = reflect_struct.meta().traits().contains(REFLECT_DEFAULT);
|
||||||
|
let constructor = if is_defaultable {
|
||||||
quote!(
|
quote!(
|
||||||
let mut __this: Self = #FQDefault::default();
|
let mut __this: Self = #FQDefault::default();
|
||||||
#(
|
#(
|
||||||
|
@ -120,16 +132,19 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
// Add FromReflect bound for each active field
|
// Add FromReflect bound for each active field
|
||||||
let mut where_from_reflect_clause = if where_clause.is_some() {
|
let where_from_reflect_clause = extend_where_clause(
|
||||||
quote! {#where_clause}
|
where_clause,
|
||||||
} else if !active_members.is_empty() {
|
&WhereClauseOptions {
|
||||||
quote! {where}
|
active_types: reflect_struct.active_types().into_boxed_slice(),
|
||||||
} else {
|
ignored_types: reflect_struct.ignored_types().into_boxed_slice(),
|
||||||
quote! {}
|
active_trait_bounds: quote!(#bevy_reflect_path::FromReflect),
|
||||||
};
|
ignored_trait_bounds: if is_defaultable {
|
||||||
where_from_reflect_clause.extend(quote! {
|
quote!()
|
||||||
#(#field_types: #bevy_reflect_path::FromReflect,)*
|
} else {
|
||||||
});
|
quote!(#FQDefault)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
|
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
|
||||||
|
|
|
@ -122,8 +122,9 @@ pub(crate) fn extend_where_clause(
|
||||||
let active_trait_bounds = &where_clause_options.active_trait_bounds;
|
let active_trait_bounds = &where_clause_options.active_trait_bounds;
|
||||||
let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds;
|
let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds;
|
||||||
|
|
||||||
let mut generic_where_clause = if where_clause.is_some() {
|
let mut generic_where_clause = if let Some(where_clause) = where_clause {
|
||||||
quote! {#where_clause}
|
let predicates = where_clause.predicates.iter();
|
||||||
|
quote! {where #(#predicates,)*}
|
||||||
} else if !(active_types.is_empty() && ignored_types.is_empty()) {
|
} else if !(active_types.is_empty() && ignored_types.is_empty()) {
|
||||||
quote! {where}
|
quote! {where}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
use bevy_reflect::prelude::*;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct NonReflect;
|
||||||
|
|
||||||
|
struct NonReflectNonDefault;
|
||||||
|
|
||||||
|
mod structs {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectGeneric<T> {
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectGeneric<T> {
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultGeneric<T> {
|
||||||
|
foo: Option<T>,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for DefaultGeneric<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
foo: None,
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectBoundGeneric<T: Clone> {
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectBoundGeneric<T: Clone> {
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultBoundGeneric<T: Clone> {
|
||||||
|
foo: Option<T>,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Default for DefaultBoundGeneric<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
foo: None,
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
foo: Option<T>,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for DefaultGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
foo: None,
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
struct ReflectGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone
|
||||||
|
{
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
struct FromReflectGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone
|
||||||
|
{
|
||||||
|
foo: T,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
struct DefaultGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone
|
||||||
|
{
|
||||||
|
foo: Option<T>,
|
||||||
|
#[reflect(ignore)]
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for DefaultGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
foo: None,
|
||||||
|
_ignored: NonReflectNonDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tuple_structs {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectGeneric<T>(T, #[reflect(ignore)] NonReflect);
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectGeneric<T>(T, #[reflect(ignore)] NonReflect);
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultGeneric<T>(Option<T>, #[reflect(ignore)] NonReflectNonDefault);
|
||||||
|
|
||||||
|
impl<T> Default for DefaultGeneric<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(None, NonReflectNonDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectBoundGeneric<T: Clone>(T, #[reflect(ignore)] NonReflect);
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectBoundGeneric<T: Clone>(T, #[reflect(ignore)] NonReflect);
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultBoundGeneric<T: Clone>(Option<T>, #[reflect(ignore)] NonReflectNonDefault);
|
||||||
|
|
||||||
|
impl<T: Clone> Default for DefaultBoundGeneric<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(None, NonReflectNonDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
struct ReflectGenericWithWhere<T>(T, #[reflect(ignore)] NonReflect)
|
||||||
|
where
|
||||||
|
T: Clone;
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
struct FromReflectGenericWithWhere<T>(T, #[reflect(ignore)] NonReflect)
|
||||||
|
where
|
||||||
|
T: Clone;
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[reflect(Default)]
|
||||||
|
struct DefaultGenericWithWhere<T>(Option<T>, #[reflect(ignore)] NonReflectNonDefault)
|
||||||
|
where
|
||||||
|
T: Clone;
|
||||||
|
|
||||||
|
impl<T> Default for DefaultGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(None, NonReflectNonDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod enums {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
enum ReflectGeneric<T> {
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
enum FromReflectGeneric<T> {
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
enum ReflectBoundGeneric<T: Clone> {
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
enum FromReflectBoundGeneric<T: Clone> {
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
enum ReflectGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
enum FromReflectGenericWithWhere<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
enum ReflectGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone
|
||||||
|
{
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, FromReflect)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
enum FromReflectGenericWithWhereNoTrailingComma<T>
|
||||||
|
where
|
||||||
|
T: Clone
|
||||||
|
{
|
||||||
|
Foo(T, #[reflect(ignore)] NonReflect),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue