Skip to content

Commit

Permalink
Add #[reflect(ignore)] for generic params
Browse files Browse the repository at this point in the history
  • Loading branch information
MrGVSV committed Jul 5, 2023
1 parent 9478432 commit dc139ea
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 200 deletions.
51 changes: 15 additions & 36 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use bit_set::BitSet;
use quote::{quote, ToTokens};
use syn::token::Comma;

use crate::generics::ReflectGenerics;
use crate::{
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
TYPE_PATH_ATTRIBUTE_NAME,
};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
parse_str, Data, DeriveInput, Field, Fields, GenericParam, Generics, Ident, LitStr, Meta, Path,
parse_str, Data, DeriveInput, Field, Fields, GenericParam, Ident, LitStr, Meta, Path,
PathSegment, Type, TypeParam, Variant,
};

Expand Down Expand Up @@ -250,7 +251,7 @@ impl<'a> ReflectDerive<'a> {
let type_path = ReflectTypePath::Internal {
ident: &input.ident,
custom_path,
generics: &input.generics,
generics: ReflectGenerics::new(&input.generics)?,
};

let meta = ReflectMeta::new(type_path, traits);
Expand Down Expand Up @@ -467,8 +468,8 @@ impl<'a> ReflectStruct<'a> {
&self.fields
}

pub fn where_clause_options(&self) -> WhereClauseOptions {
WhereClauseOptions::new(self.meta(), self.active_fields(), self.ignored_fields())
pub fn where_clause_options(&self) -> Result<WhereClauseOptions, syn::Error> {
WhereClauseOptions::new(self.meta())
}
}

Expand All @@ -491,22 +492,8 @@ impl<'a> ReflectEnum<'a> {
&self.variants
}

/// Get an iterator of fields which are exposed to the reflection API
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.variants()
.iter()
.flat_map(|variant| variant.active_fields())
}

/// Get an iterator of fields which are ignored by the reflection API
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.variants()
.iter()
.flat_map(|variant| variant.ignored_fields())
}

pub fn where_clause_options(&self) -> WhereClauseOptions {
WhereClauseOptions::new(self.meta(), self.active_fields(), self.ignored_fields())
pub fn where_clause_options(&self) -> Result<WhereClauseOptions, syn::Error> {
WhereClauseOptions::new(self.meta())
}
}

Expand Down Expand Up @@ -581,7 +568,7 @@ pub(crate) enum ReflectTypePath<'a> {
External {
path: &'a Path,
custom_path: Option<Path>,
generics: &'a Generics,
generics: ReflectGenerics,
},
/// The name of a type relative to its scope.
///
Expand All @@ -594,7 +581,7 @@ pub(crate) enum ReflectTypePath<'a> {
Internal {
ident: &'a Ident,
custom_path: Option<Path>,
generics: &'a Generics,
generics: ReflectGenerics,
},
/// Any [`syn::Type`] with only a defined `type_path` and `short_type_path`.
#[allow(dead_code)]
Expand Down Expand Up @@ -644,17 +631,10 @@ impl<'a> ReflectTypePath<'a> {
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
pub fn generics(&self) -> &'a Generics {
// Use a constant because we need to return a reference of at least 'a.
const EMPTY_GENERICS: &Generics = &Generics {
gt_token: None,
lt_token: None,
where_clause: None,
params: Punctuated::new(),
};
pub fn generics(&self) -> &ReflectGenerics {
match self {
Self::Internal { generics, .. } | Self::External { generics, .. } => generics,
_ => EMPTY_GENERICS,
_ => ReflectGenerics::EMPTY,
}
}

Expand All @@ -666,9 +646,8 @@ impl<'a> ReflectTypePath<'a> {
// (which all have to be 'static anyway).
!self
.generics()
.params
.iter()
.all(|param| matches!(param, GenericParam::Lifetime(_)))
.params()
.all(|(param, _)| matches!(param, GenericParam::Lifetime(_)))
}

/// Returns the path interpreted as a [`Path`].
Expand Down Expand Up @@ -736,10 +715,10 @@ impl<'a> ReflectTypePath<'a> {
///
/// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s.
fn reduce_generics(
generics: &Generics,
generics: &ReflectGenerics,
mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,
) -> StringExpr {
let mut params = generics.params.iter().filter_map(|param| match param {
let mut params = generics.params().filter_map(|(param, _)| match param {
GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),
GenericParam::Const(const_param) => {
let ident = &const_param.ident;
Expand Down
68 changes: 21 additions & 47 deletions crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,38 @@ use quote::{quote, ToTokens};
use syn::{Field, Ident, Lit, LitInt, LitStr, Member};

/// Implements `FromReflect` for the given struct
pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
pub(crate) fn impl_struct(
reflect_struct: &ReflectStruct,
) -> Result<proc_macro2::TokenStream, syn::Error> {
impl_struct_internal(reflect_struct, false)
}

/// Implements `FromReflect` for the given tuple struct
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
pub(crate) fn impl_tuple_struct(
reflect_struct: &ReflectStruct,
) -> Result<proc_macro2::TokenStream, syn::Error> {
impl_struct_internal(reflect_struct, true)
}

pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream {
pub(crate) fn impl_value(meta: &ReflectMeta) -> Result<proc_macro2::TokenStream, syn::Error> {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_from_reflect_clause =
extend_where_clause(where_clause, &WhereClauseOptions::new_value(meta));
quote! {
extend_where_clause(where_clause, &WhereClauseOptions::new(meta)?);
Ok(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_from_reflect_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
#FQOption::Some(#FQClone::clone(<dyn #FQAny>::downcast_ref::<#type_path #ty_generics>(<dyn #bevy_reflect_path::Reflect>::as_any(reflect))?))
}
}
}
})
}

/// Implements `FromReflect` for the given enum type
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
pub(crate) fn impl_enum(
reflect_enum: &ReflectEnum,
) -> Result<proc_macro2::TokenStream, syn::Error> {
let fqoption = FQOption.into_token_stream();

let enum_path = reflect_enum.meta().type_path();
Expand All @@ -50,24 +56,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();

// Add FromReflect bound for each active field
let where_from_reflect_clause = extend_where_clause(
where_clause,
&WhereClauseOptions::new_with_bounds(
reflect_enum.meta(),
reflect_enum.active_fields(),
reflect_enum.ignored_fields(),
|field| match &field.attrs.default {
DefaultBehavior::Default => Some(quote!(#FQDefault)),
_ => None,
},
|field| match &field.attrs.default {
DefaultBehavior::Func(_) => None,
_ => Some(quote!(#FQDefault)),
},
),
);
let where_from_reflect_clause =
extend_where_clause(where_clause, &WhereClauseOptions::new(reflect_enum.meta())?);

quote! {
Ok(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause {
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) {
Expand All @@ -80,7 +72,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
}
}
}
}
})
}

/// Container for a struct's members (field name or index) and their
Expand All @@ -96,7 +88,7 @@ impl MemberValuePair {
fn impl_struct_internal(
reflect_struct: &ReflectStruct,
is_tuple: bool,
) -> proc_macro2::TokenStream {
) -> Result<proc_macro2::TokenStream, syn::Error> {
let fqoption = FQOption.into_token_stream();

let struct_path = reflect_struct.meta().type_path();
Expand Down Expand Up @@ -146,28 +138,10 @@ fn impl_struct_internal(
// Add FromReflect bound for each active field
let where_from_reflect_clause = extend_where_clause(
where_clause,
&WhereClauseOptions::new_with_bounds(
reflect_struct.meta(),
reflect_struct.active_fields(),
reflect_struct.ignored_fields(),
|field| match &field.attrs.default {
DefaultBehavior::Default => Some(quote!(#FQDefault)),
_ => None,
},
|field| {
if is_defaultable {
None
} else {
match &field.attrs.default {
DefaultBehavior::Func(_) => None,
_ => Some(quote!(#FQDefault)),
}
}
},
),
&WhereClauseOptions::new(reflect_struct.meta())?,
);

quote! {
Ok(quote! {
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = #bevy_reflect_path::Reflect::reflect_ref(reflect) {
Expand All @@ -177,7 +151,7 @@ fn impl_struct_internal(
}
}
}
}
})
}

/// Get the collection of ignored field definitions
Expand Down
136 changes: 136 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::field_attributes::IGNORE_ALL_ATTR;
use crate::REFLECT_ATTRIBUTE_NAME;
use quote::ToTokens;
use syn::punctuated::Punctuated;
use syn::{
Attribute, GenericParam, Generics, ImplGenerics, Meta, TypeGenerics, TypeParam, WhereClause,
};

pub(crate) struct ReflectGenerics {
generics: Generics,
attrs: Vec<GenericParamAttr>,
}

impl ReflectGenerics {
pub const EMPTY: &ReflectGenerics = &ReflectGenerics {
generics: Generics {
gt_token: None,
lt_token: None,
where_clause: None,
params: Punctuated::new(),
},
attrs: Vec::new(),
};

pub fn new(generics: &Generics) -> Result<Self, syn::Error> {
let mut generics = generics.clone();

let attrs = generics
.params
.iter_mut()
.map(GenericParamAttr::from_param)
.collect::<Result<Vec<_>, syn::Error>>()?;

Ok(Self { generics, attrs })
}

pub fn params(&self) -> impl Iterator<Item = (&GenericParam, &GenericParamAttr)> {
self.generics.params.iter().zip(self.attrs.iter())
}

pub fn type_params(&self) -> impl Iterator<Item = (&TypeParam, &GenericParamAttr)> {
self.params().filter_map(|(param, attr)| match param {
GenericParam::Type(param) => Some((param, attr)),
_ => None,
})
}

pub fn split_for_impl(&self) -> (ImplGenerics, TypeGenerics, Option<&WhereClause>) {
self.generics.split_for_impl()
}
}

#[derive(Default, Clone)]
pub(crate) struct GenericParamAttr {
pub ignore: bool,
}

impl GenericParamAttr {
fn from_param(param: &mut GenericParam) -> Result<Self, syn::Error> {
match param {
GenericParam::Type(param) => Self::from_type_attrs(&mut param.attrs),
GenericParam::Lifetime(param) => Self::from_lifetime_attrs(&mut param.attrs),
GenericParam::Const(param) => Self::from_const_attrs(&mut param.attrs),
}
}

fn from_type_attrs(attrs: &mut Vec<Attribute>) -> Result<Self, syn::Error> {
let mut args = Self::default();
let mut errors: Option<syn::Error> = None;

attrs.retain(|attr| {
if attr.path().is_ident(REFLECT_ATTRIBUTE_NAME) {
if let Err(err) = parse_meta(&mut args, &attr.meta) {
if let Some(ref mut error) = errors {
error.combine(err);
} else {
errors = Some(err);
}
}

false
} else {
true
}
});

if let Some(error) = errors {
Err(error)
} else {
Ok(args)
}
}

fn from_lifetime_attrs(attrs: &mut [Attribute]) -> Result<Self, syn::Error> {
match attrs
.iter()
.find(|attr| attr.path().is_ident(REFLECT_ATTRIBUTE_NAME))
{
Some(attr) => Err(syn::Error::new_spanned(
attr,
format!(
"{REFLECT_ATTRIBUTE_NAME} attributes cannot be used on lifetime parameters"
),
)),
None => Ok(Self::default()),
}
}

fn from_const_attrs(attrs: &mut [Attribute]) -> Result<Self, syn::Error> {
match attrs
.iter()
.find(|attr| attr.path().is_ident(REFLECT_ATTRIBUTE_NAME))
{
Some(attr) => Err(syn::Error::new_spanned(
attr,
format!("{REFLECT_ATTRIBUTE_NAME} attributes cannot be used on const parameters"),
)),
None => Ok(Self::default()),
}
}
}

fn parse_meta(args: &mut GenericParamAttr, meta: &Meta) -> Result<(), syn::Error> {
let meta = meta.require_list()?;
meta.parse_nested_meta(|meta| {
if meta.path.is_ident(IGNORE_ALL_ATTR) {
args.ignore = true;
Ok(())
} else {
Err(syn::Error::new_spanned(
&meta.path,
format!("unknown attribute '{}'", meta.path.to_token_stream()),
))
}
})
}
Loading

0 comments on commit dc139ea

Please sign in to comment.