Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reflection: replace impl_reflect_struct with impl_reflect #11437

Merged
merged 8 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
//! the derive helper attribute for `Reflect`, which looks like:
//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`.

use crate::derive_data::ReflectTraitToImpl;
use crate::utility;
use bevy_macro_utils::fq_std::{FQAny, FQOption};
use proc_macro2::{Ident, Span};
use quote::quote_spanned;
use syn::parse::{Parse, ParseStream};
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
Expand Down Expand Up @@ -217,7 +218,7 @@ pub(crate) struct ReflectTraits {
impl ReflectTraits {
pub fn from_metas(
metas: Punctuated<Meta, Comma>,
is_from_reflect_derive: bool,
trait_: ReflectTraitToImpl,
) -> Result<Self, syn::Error> {
let mut traits = ReflectTraits::default();
for meta in &metas {
Expand Down Expand Up @@ -294,7 +295,7 @@ impl ReflectTraits {
// Override `lit` if this is a `FromReflect` derive.
// This typically means a user is opting out of the default implementation
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
is_from_reflect_derive
(trait_ == ReflectTraitToImpl::FromReflect)
.then(|| LitBool::new(true, Span::call_site()))
.unwrap_or_else(|| lit.clone())
})?);
Expand All @@ -311,6 +312,10 @@ impl ReflectTraits {
Ok(traits)
}

pub fn parse(input: ParseStream, trait_: ReflectTraitToImpl) -> syn::Result<Self> {
ReflectTraits::from_metas(Punctuated::parse_terminated(input)?, trait_)
}

/// 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 {
Expand Down Expand Up @@ -418,12 +423,6 @@ impl ReflectTraits {
}
}

impl Parse for ReflectTraits {
fn parse(input: ParseStream) -> syn::Result<Self> {
ReflectTraits::from_metas(Punctuated::<Meta, Comma>::parse_terminated(input)?, false)
}
}

/// Adds an identifier to a vector of identifiers if it is not already present.
///
/// Returns an error if the identifier already exists in the list.
Expand Down
54 changes: 51 additions & 3 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use crate::container_attributes::{FromReflectAttrs, ReflectTraits};
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
use crate::type_path::parse_path_no_leading_colon;
Expand Down Expand Up @@ -140,10 +142,46 @@ enum ReflectMode {
Value,
}

/// How the macro was invoked.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ReflectImplSource {
ImplRemoteType,
DeriveLocalType,
}

/// Which trait the macro explicitly implements.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ReflectTraitToImpl {
Reflect,
FromReflect,
TypePath,
}

/// The provenance of a macro invocation.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) struct ReflectProvenance {
pub source: ReflectImplSource,
pub trait_: ReflectTraitToImpl,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshed: Rather than having to add the _ suffix, maybe we could rename this to trait_target (maybe even renaming ReflectTraitToImpl to ReflectTraitTarget) or trait_to_impl or something similar.

}

impl fmt::Display for ReflectProvenance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::{ReflectImplSource as S, ReflectTraitToImpl as T};
let str = match (self.source, self.trait_) {
(S::ImplRemoteType, T::Reflect) => "`impl_reflect`",
(S::DeriveLocalType, T::Reflect) => "`#[derive(Reflect)]`",
(S::DeriveLocalType, T::FromReflect) => "`#[derive(FromReflect)]`",
(S::DeriveLocalType, T::TypePath) => "`#[derive(TypePath)]`",
(S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),
};
f.write_str(str)
}
}

impl<'a> ReflectDerive<'a> {
pub fn from_input(
input: &'a DeriveInput,
is_from_reflect_derive: bool,
provenance: ReflectProvenance,
) -> Result<Self, syn::Error> {
let mut traits = ReflectTraits::default();
// Should indicate whether `#[reflect_value]` was used.
Expand All @@ -169,7 +207,7 @@ impl<'a> ReflectDerive<'a> {
reflect_mode = Some(ReflectMode::Normal);
let new_traits = ReflectTraits::from_metas(
meta_list.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)?,
is_from_reflect_derive,
provenance.trait_,
)?;
traits.merge(new_traits)?;
}
Expand All @@ -184,7 +222,7 @@ impl<'a> ReflectDerive<'a> {
reflect_mode = Some(ReflectMode::Value);
let new_traits = ReflectTraits::from_metas(
meta_list.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)?,
is_from_reflect_derive,
provenance.trait_,
)?;
traits.merge(new_traits)?;
}
Expand Down Expand Up @@ -264,6 +302,16 @@ impl<'a> ReflectDerive<'a> {

let meta = ReflectMeta::new(type_path, traits);

if provenance.source == ReflectImplSource::ImplRemoteType
&& meta.traits.type_path_attrs().should_auto_derive()
soqb marked this conversation as resolved.
Show resolved Hide resolved
&& !meta.type_path().has_custom_path()
{
return Err(syn::Error::new(
meta.type_path().span(),
format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}")
));
Comment on lines +303 to +306
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

}

#[cfg(feature = "documentation")]
let meta = meta.with_docs(doc);

Expand Down
180 changes: 80 additions & 100 deletions crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ mod utility;

use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
use container_attributes::ReflectTraits;
use derive_data::ReflectTypePath;
use derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl, ReflectTypePath};
use proc_macro::TokenStream;
use quote::quote;
use reflect_value::ReflectValueDef;
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput};
use type_path::NamedTypePathDef;

Expand All @@ -45,6 +44,62 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
pub(crate) static TYPE_PATH_ATTRIBUTE_NAME: &str = "type_path";
pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name";

/// Used both for [`impl_reflect`] and [`derive_reflect`].
fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStream {
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source,
trait_: ReflectTraitToImpl::Reflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};

let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_struct(&struct_data))
} else {
None
},
),
ReflectDerive::TupleStruct(struct_data) => (
impls::impl_tuple_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_tuple_struct(&struct_data))
} else {
None
},
),
ReflectDerive::Enum(enum_data) => (
impls::impl_enum(&enum_data),
if enum_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_enum(&enum_data))
} else {
None
},
),
ReflectDerive::Value(meta) => (
impls::impl_value(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_value(&meta))
} else {
None
},
),
};

TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
};
})
}

/// The main derive macro used by `bevy_reflect` for deriving its `Reflect` trait.
///
/// This macro can be used on all structs and enums (unions are not supported).
Expand Down Expand Up @@ -159,53 +214,7 @@ pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name";
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, type_path, type_name))]
pub fn derive_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);

let derive_data = match ReflectDerive::from_input(&ast, false) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};

let (reflect_impls, from_reflect_impl) = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
impls::impl_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_struct(&struct_data))
} else {
None
},
),
ReflectDerive::TupleStruct(struct_data) => (
impls::impl_tuple_struct(&struct_data),
if struct_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_tuple_struct(&struct_data))
} else {
None
},
),
ReflectDerive::Enum(enum_data) => (
impls::impl_enum(&enum_data),
if enum_data.meta().from_reflect().should_auto_derive() {
Some(from_reflect::impl_enum(&enum_data))
} else {
None
},
),
ReflectDerive::Value(meta) => (
impls::impl_value(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_value(&meta))
} else {
None
},
),
};

TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
};
})
match_reflect_impls(ast, ReflectImplSource::DeriveLocalType)
}

/// Derives the `FromReflect` trait.
Expand Down Expand Up @@ -238,7 +247,13 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream {
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);

let derive_data = match ReflectDerive::from_input(&ast, true) {
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::FromReflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
Expand Down Expand Up @@ -277,7 +292,13 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
#[proc_macro_derive(TypePath, attributes(type_path, type_name))]
pub fn derive_type_path(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(&ast, false) {
let derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::TypePath,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
Expand Down Expand Up @@ -392,7 +413,7 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
/// [deriving `Reflect`]: Reflect
#[proc_macro]
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
let def = parse_macro_input!(input with ReflectValueDef::parse_reflect);

let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none() && def.custom_path.is_none() {
Expand Down Expand Up @@ -425,8 +446,9 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
/// the definitions of cannot be altered.
///
/// This macro is an alternative to [`impl_reflect_value!`] and [`impl_from_reflect_value!`]
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect_struct`,
/// as this macro will do the job of both. This macro implements them as `Struct` types,
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect`,
/// as this macro will do the job of both. This macro implements them using one of the reflect
/// variant traits (`bevy_reflect::{Struct, TupleStruct, Enum}`, etc.),
/// which have greater functionality. The type being reflected must be in scope, as you cannot
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
///
Expand All @@ -443,7 +465,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
/// ```ignore (bevy_reflect is not accessible from this crate)
/// use bevy::prelude::Vec3;
///
/// impl_reflect_struct!(
/// impl_reflect!(
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// #[type_path = "bevy::prelude"]
/// struct Vec3 {
Expand All @@ -454,51 +476,9 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
/// );
/// ```
#[proc_macro]
pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
pub fn impl_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(&ast, false) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};

let output = match derive_data {
ReflectDerive::Struct(struct_data) => {
if !struct_data.meta().type_path().has_custom_path() {
return syn::Error::new(
struct_data.meta().type_path().span(),
format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using `impl_reflect_struct`")
)
.into_compile_error()
.into();
}

let impl_struct = impls::impl_struct(&struct_data);
let impl_from_struct = from_reflect::impl_struct(&struct_data);

quote! {
#impl_struct
#impl_from_struct
}
}
ReflectDerive::TupleStruct(..) => syn::Error::new(
ast.span(),
"impl_reflect_struct does not support tuple structs",
)
.into_compile_error(),
ReflectDerive::UnitStruct(..) => syn::Error::new(
ast.span(),
"impl_reflect_struct does not support unit structs",
)
.into_compile_error(),
_ => syn::Error::new(ast.span(), "impl_reflect_struct only supports structs")
.into_compile_error(),
};

TokenStream::from(quote! {
const _: () = {
#output
};
})
match_reflect_impls(ast, ReflectImplSource::ImplRemoteType)
}

/// A macro used to generate a `FromReflect` trait implementation for the given type.
Expand All @@ -523,7 +503,7 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
/// [derives `Reflect`]: Reflect
#[proc_macro]
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
let def = parse_macro_input!(input with ReflectValueDef::parse_from_reflect);

let default_name = &def.type_path.segments.last().unwrap().ident;
let type_path = if def.type_path.leading_colon.is_none()
Expand Down
Loading
Loading