Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
use syn::{
parse::{Parse, ParseStream, Peek},
punctuated::Punctuated,
};
/// Returns a [`syn::parse::Parser`] which parses a stream of zero or more occurrences of `T`
/// separated by punctuation of type `P`, with optional trailing punctuation.
///
/// This is functionally the same as [`Punctuated::parse_terminated`],
/// but accepts a closure rather than a function pointer.
pub(crate) fn terminated_parser<T, P, F: FnMut(ParseStream) -> syn::Result<T>>(
terminator: P,
mut parser: F,
) -> impl FnOnce(ParseStream) -> syn::Result<Punctuated<T, P::Token>>
where
P: Peek,
P::Token: Parse,
{
let _ = terminator;
move |stream: ParseStream| {
let mut punctuated = Punctuated::new();
loop {
if stream.is_empty() {
break;
}
let value = parser(stream)?;
punctuated.push_value(value);
if stream.is_empty() {
break;
}
let punct = stream.parse()?;
punctuated.push_punct(punct);
}
Ok(punctuated)
}
}

View File

@@ -0,0 +1,620 @@
//! 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, ...)]`.
use crate::{
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
derive_data::ReflectTraitToImpl,
};
use bevy_macro_utils::fq_std::{FQAny, FQClone, FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote_spanned;
use syn::{
ext::IdentExt, parenthesized, parse::ParseStream, spanned::Spanned, token, Expr, LitBool,
MetaList, MetaNameValue, Path, Token, WhereClause,
};
mod kw {
syn::custom_keyword!(from_reflect);
syn::custom_keyword!(type_path);
syn::custom_keyword!(Debug);
syn::custom_keyword!(PartialEq);
syn::custom_keyword!(Hash);
syn::custom_keyword!(Clone);
syn::custom_keyword!(no_field_bounds);
syn::custom_keyword!(opaque);
}
// The "special" trait idents that are used internally for reflection.
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
const DEBUG_ATTR: &str = "Debug";
const PARTIAL_EQ_ATTR: &str = "PartialEq";
const HASH_ATTR: &str = "Hash";
// The traits listed below are not considered "special" (i.e. they use the `ReflectMyTrait` syntax)
// but useful to know exist nonetheless
pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault";
// Attributes for `FromReflect` implementation
const FROM_REFLECT_ATTR: &str = "from_reflect";
// Attributes for `TypePath` implementation
const TYPE_PATH_ATTR: &str = "type_path";
// The error message to show when a trait/type is specified multiple times
const CONFLICTING_TYPE_DATA_MESSAGE: &str = "conflicting type data registration";
/// A marker for trait implementations registered via the `Reflect` derive macro.
#[derive(Clone, Default)]
pub(crate) enum TraitImpl {
/// The trait is not registered as implemented.
#[default]
NotImplemented,
/// The trait is registered as implemented.
Implemented(Span),
/// The trait is registered with a custom function rather than an actual implementation.
Custom(Path, Span),
}
impl TraitImpl {
/// Merges this [`TraitImpl`] with another.
///
/// Update `self` with whichever value is not [`TraitImpl::NotImplemented`].
/// If `other` is [`TraitImpl::NotImplemented`], then `self` is not modified.
/// An error is returned if neither value is [`TraitImpl::NotImplemented`].
pub fn merge(&mut self, other: TraitImpl) -> Result<(), syn::Error> {
match (&self, other) {
(TraitImpl::NotImplemented, value) => {
*self = value;
Ok(())
}
(_, TraitImpl::NotImplemented) => Ok(()),
(_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => {
Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE))
}
}
}
}
/// A collection of attributes used for deriving `FromReflect`.
#[derive(Clone, Default)]
pub(crate) struct FromReflectAttrs {
auto_derive: Option<LitBool>,
}
impl FromReflectAttrs {
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
pub fn should_auto_derive(&self) -> bool {
self.auto_derive.as_ref().is_none_or(LitBool::value)
}
}
/// A collection of attributes used for deriving `TypePath` via the `Reflect` derive.
///
/// Note that this differs from the attributes used by the `TypePath` derive itself,
/// which look like `[type_path = "my_crate::foo"]`.
/// The attributes used by reflection take the form `#[reflect(type_path = false)]`.
///
/// These attributes should only be used for `TypePath` configuration specific to
/// deriving `Reflect`.
#[derive(Clone, Default)]
pub(crate) struct TypePathAttrs {
auto_derive: Option<LitBool>,
}
impl TypePathAttrs {
/// Returns true if `TypePath` should be automatically derived as part of the `Reflect` derive.
pub fn should_auto_derive(&self) -> bool {
self.auto_derive.as_ref().is_none_or(LitBool::value)
}
}
/// 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:
/// * `Debug`
/// * `Hash`
/// * `PartialEq`
///
/// 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 (bevy_reflect is not accessible from this crate)
/// // 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 (bevy_reflect is not accessible from this crate)
/// // `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 (bevy_reflect is not accessible from this crate)
/// // This function acts as our `Hash` implementation and
/// // corresponds to the `Reflect::reflect_hash` method.
/// fn get_hash(foo: &Foo) -> Option<u64> {
/// 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, Clone)]
pub(crate) struct ContainerAttributes {
clone: TraitImpl,
debug: TraitImpl,
hash: TraitImpl,
partial_eq: TraitImpl,
from_reflect_attrs: FromReflectAttrs,
type_path_attrs: TypePathAttrs,
custom_where: Option<WhereClause>,
no_field_bounds: bool,
custom_attributes: CustomAttributes,
is_opaque: bool,
idents: Vec<Ident>,
}
impl ContainerAttributes {
/// Parse a comma-separated list of container attributes.
///
/// # Example
/// - `Hash, Debug(custom_debug), MyTrait`
pub fn parse_terminated(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
terminated_parser(Token![,], |stream| {
self.parse_container_attribute(stream, trait_)
})(input)?;
Ok(())
}
/// Parse the contents of a `#[reflect(...)]` attribute into a [`ContainerAttributes`] instance.
///
/// # Example
/// - `#[reflect(Hash, Debug(custom_debug), MyTrait)]`
/// - `#[reflect(no_field_bounds)]`
pub fn parse_meta_list(
&mut self,
meta: &MetaList,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
meta.parse_args_with(|stream: ParseStream| self.parse_terminated(stream, trait_))
}
/// Parse a single container attribute.
fn parse_container_attribute(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![@]) {
self.custom_attributes.parse_custom_attribute(input)
} else if lookahead.peek(Token![where]) {
self.parse_custom_where(input)
} else if lookahead.peek(kw::from_reflect) {
self.parse_from_reflect(input, trait_)
} else if lookahead.peek(kw::type_path) {
self.parse_type_path(input, trait_)
} else if lookahead.peek(kw::opaque) {
self.parse_opaque(input)
} else if lookahead.peek(kw::no_field_bounds) {
self.parse_no_field_bounds(input)
} else if lookahead.peek(kw::Clone) {
self.parse_clone(input)
} else if lookahead.peek(kw::Debug) {
self.parse_debug(input)
} else if lookahead.peek(kw::Hash) {
self.parse_hash(input)
} else if lookahead.peek(kw::PartialEq) {
self.parse_partial_eq(input)
} else if lookahead.peek(Ident::peek_any) {
self.parse_ident(input)
} else {
Err(lookahead.error())
}
}
/// Parse an ident (for registration).
///
/// Examples:
/// - `#[reflect(MyTrait)]` (registers `ReflectMyTrait`)
fn parse_ident(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<Ident>()?;
if input.peek(token::Paren) {
return Err(syn::Error::new(ident.span(), format!(
"only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {HASH_ATTR:?}] may specify custom functions",
)));
}
let ident_name = ident.to_string();
// Create the reflect ident
let mut reflect_ident = crate::ident::get_reflect_ident(&ident_name);
// We set the span to the old ident so any compile errors point to that ident instead
reflect_ident.set_span(ident.span());
add_unique_ident(&mut self.idents, reflect_ident)?;
Ok(())
}
/// Parse `clone` attribute.
///
/// Examples:
/// - `#[reflect(Clone)]`
/// - `#[reflect(Clone(custom_clone_fn))]`
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Clone>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.clone.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.clone = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `Debug` registration.
///
/// Examples:
/// - `#[reflect(Debug)]`
/// - `#[reflect(Debug(custom_debug_fn))]`
fn parse_debug(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Debug>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.debug.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.debug = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `PartialEq` registration.
///
/// Examples:
/// - `#[reflect(PartialEq)]`
/// - `#[reflect(PartialEq(custom_partial_eq_fn))]`
fn parse_partial_eq(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::PartialEq>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.partial_eq.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.partial_eq = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse special `Hash` registration.
///
/// Examples:
/// - `#[reflect(Hash)]`
/// - `#[reflect(Hash(custom_hash_fn))]`
fn parse_hash(&mut self, input: ParseStream) -> syn::Result<()> {
let ident = input.parse::<kw::Hash>()?;
if input.peek(token::Paren) {
let content;
parenthesized!(content in input);
let path = content.parse::<Path>()?;
self.hash.merge(TraitImpl::Custom(path, ident.span))?;
} else {
self.hash = TraitImpl::Implemented(ident.span);
}
Ok(())
}
/// Parse `opaque` attribute.
///
/// Examples:
/// - `#[reflect(opaque)]`
fn parse_opaque(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<kw::opaque>()?;
self.is_opaque = true;
Ok(())
}
/// Parse `no_field_bounds` attribute.
///
/// Examples:
/// - `#[reflect(no_field_bounds)]`
fn parse_no_field_bounds(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<kw::no_field_bounds>()?;
self.no_field_bounds = true;
Ok(())
}
/// Parse `where` attribute.
///
/// Examples:
/// - `#[reflect(where T: Debug)]`
fn parse_custom_where(&mut self, input: ParseStream) -> syn::Result<()> {
self.custom_where = Some(input.parse()?);
Ok(())
}
/// Parse `from_reflect` attribute.
///
/// Examples:
/// - `#[reflect(from_reflect = false)]`
fn parse_from_reflect(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let extracted_bool = extract_bool(&pair.value, |lit| {
// 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.
if trait_ == ReflectTraitToImpl::FromReflect {
LitBool::new(true, Span::call_site())
} else {
lit.clone()
}
})?;
if let Some(existing) = &self.from_reflect_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{FROM_REFLECT_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.from_reflect_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
/// Parse `type_path` attribute.
///
/// Examples:
/// - `#[reflect(type_path = false)]`
fn parse_type_path(
&mut self,
input: ParseStream,
trait_: ReflectTraitToImpl,
) -> syn::Result<()> {
let pair = input.parse::<MetaNameValue>()?;
let extracted_bool = extract_bool(&pair.value, |lit| {
// 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.
if trait_ == ReflectTraitToImpl::TypePath {
LitBool::new(true, Span::call_site())
} else {
lit.clone()
}
})?;
if let Some(existing) = &self.type_path_attrs.auto_derive {
if existing.value() != extracted_bool.value() {
return Err(syn::Error::new(
extracted_bool.span(),
format!("`{TYPE_PATH_ATTR}` already set to {}", existing.value()),
));
}
} else {
self.type_path_attrs.auto_derive = Some(extracted_bool);
}
Ok(())
}
/// 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)
}
/// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`).
pub fn idents(&self) -> &[Ident] {
&self.idents
}
/// The `FromReflect` configuration found within `#[reflect(...)]` attributes on this type.
#[expect(
clippy::wrong_self_convention,
reason = "Method returns `FromReflectAttrs`, does not actually convert data."
)]
pub fn from_reflect_attrs(&self) -> &FromReflectAttrs {
&self.from_reflect_attrs
}
/// The `TypePath` configuration found within `#[reflect(...)]` attributes on this type.
pub fn type_path_attrs(&self) -> &TypePathAttrs {
&self.type_path_attrs
}
/// Returns the implementation of `PartialReflect::reflect_hash` as a `TokenStream`.
///
/// If `Hash` was not registered, returns `None`.
pub fn get_hash_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
match &self.hash {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn reflect_hash(&self) -> #FQOption<u64> {
use ::core::hash::{Hash, Hasher};
let mut hasher = #bevy_reflect_path::utility::reflect_hasher();
Hash::hash(&#FQAny::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
#FQOption::Some(Hasher::finish(&hasher))
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn reflect_hash(&self) -> #FQOption<u64> {
#FQOption::Some(#impl_fn(self))
}
}),
TraitImpl::NotImplemented => None,
}
}
/// Returns the implementation of `PartialReflect::reflect_partial_eq` as a `TokenStream`.
///
/// If `PartialEq` was not registered, returns `None`.
pub fn get_partial_eq_impl(
&self,
bevy_reflect_path: &Path,
) -> Option<proc_macro2::TokenStream> {
match &self.partial_eq {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
let value = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value);
if let #FQOption::Some(value) = value {
#FQOption::Some(::core::cmp::PartialEq::eq(self, value))
} else {
#FQOption::Some(false)
}
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
#FQOption::Some(#impl_fn(self, value))
}
}),
TraitImpl::NotImplemented => None,
}
}
/// Returns the implementation of `PartialReflect::debug` as a `TokenStream`.
///
/// If `Debug` was not registered, returns `None`.
pub fn get_debug_impl(&self) -> Option<proc_macro2::TokenStream> {
match &self.debug {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
::core::fmt::Debug::fmt(self, f)
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#impl_fn(self, f)
}
}),
TraitImpl::NotImplemented => None,
}
}
pub fn get_clone_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
match &self.clone {
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
#[inline]
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self)))
}
}),
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
#[inline]
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#impl_fn(self)))
}
}),
TraitImpl::NotImplemented => None,
}
}
pub fn custom_attributes(&self) -> &CustomAttributes {
&self.custom_attributes
}
/// The custom where configuration found within `#[reflect(...)]` attributes on this type.
pub fn custom_where(&self) -> Option<&WhereClause> {
self.custom_where.as_ref()
}
/// Returns true if the `no_field_bounds` attribute was found on this type.
pub fn no_field_bounds(&self) -> bool {
self.no_field_bounds
}
/// Returns true if the `opaque` attribute was found on this type.
pub fn is_opaque(&self) -> bool {
self.is_opaque
}
}
/// 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.
fn add_unique_ident(idents: &mut Vec<Ident>, ident: Ident) -> Result<(), syn::Error> {
let ident_name = ident.to_string();
if idents.iter().any(|i| i == ident_name.as_str()) {
return Err(syn::Error::new(ident.span(), CONFLICTING_TYPE_DATA_MESSAGE));
}
idents.push(ident);
Ok(())
}
/// Extract a boolean value from an expression.
///
/// The mapper exists so that the caller can conditionally choose to use the given
/// value or supply their own.
fn extract_bool(
value: &Expr,
mut mapper: impl FnMut(&LitBool) -> LitBool,
) -> Result<LitBool, syn::Error> {
match value {
Expr::Lit(syn::ExprLit {
lit: syn::Lit::Bool(lit),
..
}) => Ok(mapper(lit)),
_ => Err(syn::Error::new(value.span(), "Expected a boolean value")),
}
}

View File

@@ -0,0 +1,41 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse::ParseStream, Expr, Path, Token};
#[derive(Default, Clone)]
pub(crate) struct CustomAttributes {
attributes: Vec<Expr>,
}
impl CustomAttributes {
/// Generates a `TokenStream` for `CustomAttributes` construction.
pub fn to_tokens(&self, bevy_reflect_path: &Path) -> TokenStream {
let attributes = self.attributes.iter().map(|value| {
quote! {
.with_attribute(#value)
}
});
quote! {
#bevy_reflect_path::attributes::CustomAttributes::default()
#(#attributes)*
}
}
/// Inserts a custom attribute into the list.
pub fn push(&mut self, value: Expr) -> syn::Result<()> {
self.attributes.push(value);
Ok(())
}
/// Parse `@` (custom attribute) attribute.
///
/// Examples:
/// - `#[reflect(@Foo))]`
/// - `#[reflect(@Bar::baz("qux"))]`
/// - `#[reflect(@0..256u8)]`
pub fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
input.parse::<Token![@]>()?;
self.push(input.parse()?)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
//! Contains code related to documentation reflection (requires the `documentation` feature).
use bevy_macro_utils::fq_std::FQOption;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Attribute, Expr, ExprLit, Lit, Meta};
/// A struct used to represent a type's documentation, if any.
///
/// When converted to a [`TokenStream`], this will output an `Option<String>`
/// containing the collection of doc comments.
#[derive(Default, Clone)]
pub(crate) struct Documentation {
docs: Vec<String>,
}
impl Documentation {
/// Create a new [`Documentation`] from a type's attributes.
///
/// This will collect all `#[doc = "..."]` attributes, including the ones generated via `///` and `//!`.
pub fn from_attributes<'a>(attributes: impl IntoIterator<Item = &'a Attribute>) -> Self {
let docs = attributes
.into_iter()
.filter_map(|attr| match &attr.meta {
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
if let Expr::Lit(ExprLit {
lit: Lit::Str(lit), ..
}) = &pair.value
{
Some(lit.value())
} else {
None
}
}
_ => None,
})
.collect();
Self { docs }
}
/// The full docstring, if any.
pub fn doc_string(&self) -> Option<String> {
if self.docs.is_empty() {
return None;
}
let len = self.docs.len();
Some(
self.docs
.iter()
.enumerate()
.map(|(index, doc)| {
if index < len - 1 {
format!("{doc}\n")
} else {
doc.to_owned()
}
})
.collect(),
)
}
/// Push a new docstring to the collection
pub fn push(&mut self, doc: String) {
self.docs.push(doc);
}
}
impl ToTokens for Documentation {
fn to_tokens(&self, tokens: &mut TokenStream) {
if let Some(doc) = self.doc_string() {
quote!(#FQOption::Some(#doc)).to_tokens(tokens);
} else {
quote!(#FQOption::None).to_tokens(tokens);
}
}
}

View File

@@ -0,0 +1,394 @@
use crate::field_attributes::CloneBehavior;
use crate::{
derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,
ident::ident_or_index,
};
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption, FQResult};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
pub(crate) struct EnumVariantOutputData {
/// The names of each variant as a string.
///
/// For example, `Some` and `None` for the `Option` enum.
pub variant_names: Vec<String>,
/// The pattern matching portion of each variant.
///
/// For example, `Option::Some { 0: _0 }` and `Option::None {}` for the `Option` enum.
pub variant_patterns: Vec<TokenStream>,
/// The constructor portion of each variant.
///
/// For example, `Option::Some { 0: value }` and `Option::None {}` for the `Option` enum.
pub variant_constructors: Vec<TokenStream>,
}
#[derive(Copy, Clone)]
pub(crate) struct VariantField<'a, 'b> {
/// The alias for the field.
///
/// This should be used whenever the field needs to be referenced in a token stream.
pub alias: &'a Ident,
/// The name of the variant that contains the field.
pub variant_name: &'a str,
/// The field data.
pub field: &'a StructField<'b>,
}
/// Trait used to control how enum variants are built.
pub(crate) trait VariantBuilder: Sized {
/// Returns the enum data.
fn reflect_enum(&self) -> &ReflectEnum;
/// Returns a token stream that accesses a field of a variant as an `Option<dyn Reflect>`.
///
/// The default implementation of this method will return a token stream
/// which gets the field dynamically so as to support `dyn Enum`.
///
/// # Parameters
/// * `this`: The identifier of the enum
/// * `field`: The field to access
fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {
match &field.field.data.ident {
Some(field_ident) => {
let name = field_ident.to_string();
quote!(#this.field(#name))
}
None => {
if let Some(field_index) = field.field.reflection_index {
quote!(#this.field_at(#field_index))
} else {
quote!(::core::compile_error!(
"internal bevy_reflect error: field should be active"
))
}
}
}
}
/// Returns a token stream that unwraps a field of a variant as a `&dyn Reflect`
/// (from an `Option<dyn Reflect>`).
///
/// # Parameters
/// * `field`: The field to access
fn unwrap_field(&self, field: VariantField) -> TokenStream;
/// Returns a token stream that constructs a field of a variant as a concrete type
/// (from a `&dyn Reflect`).
///
/// # Parameters
/// * `field`: The field to access
fn construct_field(&self, field: VariantField) -> TokenStream;
/// Returns a token stream that constructs an instance of an active field.
///
/// # Parameters
/// * `this`: The identifier of the enum
/// * `field`: The field to access
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
let field_accessor = self.access_field(this, field);
let alias = field.alias;
let field_ty = field.field.reflected_type();
let field_constructor = self.construct_field(field);
let construction = match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#path()
}
},
DefaultBehavior::Default => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#FQDefault::default()
}
},
DefaultBehavior::Required => {
let field_unwrapper = self.unwrap_field(field);
quote! {{
// `#alias` is used by both the unwrapper and constructor
let #alias = #field_accessor;
let #alias = #field_unwrapper;
#field_constructor
}}
}
};
if field.field.attrs().remote.is_some() {
quote! {
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
}
} else {
construction
}
}
/// Returns a token stream that constructs an instance of an ignored field.
///
/// # Parameters
/// * `field`: The field to access
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! { #path() },
_ => quote! { #FQDefault::default() },
}
}
/// Builds the enum variant output data.
fn build(&self, this: &Ident) -> EnumVariantOutputData {
let variants = self.reflect_enum().variants();
let mut variant_names = Vec::with_capacity(variants.len());
let mut variant_patterns = Vec::with_capacity(variants.len());
let mut variant_constructors = Vec::with_capacity(variants.len());
for variant in variants {
let variant_ident = &variant.data.ident;
let variant_name = variant_ident.to_string();
let variant_path = self.reflect_enum().get_unit(variant_ident);
let fields = variant.fields();
let mut field_patterns = Vec::with_capacity(fields.len());
let mut field_constructors = Vec::with_capacity(fields.len());
for field in fields {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let alias = format_ident!("_{}", member);
let variant_field = VariantField {
alias: &alias,
variant_name: &variant_name,
field,
};
let value = if field.attrs.ignore.is_ignored() {
self.on_ignored_field(variant_field)
} else {
self.on_active_field(this, variant_field)
};
field_patterns.push(quote! {
#member: #alias
});
field_constructors.push(quote! {
#member: #value
});
}
let pattern = quote! {
#variant_path { #( #field_patterns ),* }
};
let constructor = quote! {
#variant_path {
#( #field_constructors ),*
}
};
variant_names.push(variant_name);
variant_patterns.push(pattern);
variant_constructors.push(constructor);
}
EnumVariantOutputData {
variant_names,
variant_patterns,
variant_constructors,
}
}
}
/// Generates the enum variant output data needed to build the `FromReflect::from_reflect` implementation.
pub(crate) struct FromReflectVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> FromReflectVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#alias?)
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = field.field.reflected_type();
let alias = field.alias;
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?
}
}
}
/// Generates the enum variant output data needed to build the `PartialReflect::try_apply` implementation.
pub(crate) struct TryApplyVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> TryApplyVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let VariantField {
alias,
variant_name,
field,
..
} = field;
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_name = match &field.data.ident {
Some(ident) => format!("{ident}"),
None => format!(".{}", field.declaration_index),
};
quote! {
#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
variant_name: ::core::convert::Into::into(#variant_name),
field_name: ::core::convert::Into::into(#field_name)
})?
}
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let alias = field.alias;
let field_ty = field.field.reflected_type();
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)
.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)
),
to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())
})?
}
}
}
/// Generates the enum variant output data needed to build the `Reflect::reflect_clone` implementation.
pub(crate) struct ReflectCloneVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> ReflectCloneVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn access_field(&self, _ident: &Ident, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#FQOption::Some(#alias))
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#alias.unwrap())
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = field.field.reflected_type();
let alias = field.alias;
let alias = match &field.field.attrs.remote {
Some(wrapper_ty) => {
quote! {
<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper(#alias)
}
}
None => alias.to_token_stream(),
};
match &field.field.attrs.clone {
CloneBehavior::Default => {
quote! {
#bevy_reflect_path::PartialReflect::reflect_clone(#alias)?
.take()
.map_err(|value| #bevy_reflect_path::ReflectCloneError::FailedDowncast {
expected: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
<#field_ty as #bevy_reflect_path::TypePath>::type_path()
),
received: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Owned(
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(
#bevy_reflect_path::DynamicTypePath::reflect_type_path(&*value)
)
),
})?
}
}
CloneBehavior::Trait => {
quote! {
#FQClone::clone(#alias)
}
}
CloneBehavior::Func(clone_fn) => {
quote! {
#clone_fn(#alias)
}
}
}
}
fn on_active_field(&self, _this: &Ident, field: VariantField) -> TokenStream {
self.construct_field(field)
}
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let variant_name = field.variant_name;
let alias = field.alias;
match &field.field.attrs.clone {
CloneBehavior::Default => {
let field_id = field.field.field_id(bevy_reflect_path);
quote! {
return #FQResult::Err(
#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {
field: #field_id,
variant: #FQOption::Some(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#variant_name)),
container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(<Self as #bevy_reflect_path::TypePath>::type_path())
}
)
}
}
CloneBehavior::Trait => quote! { #FQClone::clone(#alias) },
CloneBehavior::Func(clone_fn) => quote! { #clone_fn() },
}
}
}

View File

@@ -0,0 +1,274 @@
//! 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::{
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
REFLECT_ATTRIBUTE_NAME,
};
use quote::ToTokens;
use syn::{parse::ParseStream, Attribute, LitStr, Meta, Token, Type};
mod kw {
syn::custom_keyword!(ignore);
syn::custom_keyword!(skip_serializing);
syn::custom_keyword!(clone);
syn::custom_keyword!(default);
syn::custom_keyword!(remote);
}
pub(crate) const IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
pub(crate) const IGNORE_ALL_ATTR: &str = "ignore";
pub(crate) const DEFAULT_ATTR: &str = "default";
pub(crate) const CLONE_ATTR: &str = "clone";
/// Stores data about if the field should be visible via the Reflect and serialization interfaces
///
/// Note the relationship between serialization and reflection is such that a member must be reflected in order to be serialized.
/// In boolean logic this is described as: `is_serialized -> is_reflected`, this means we can reflect something without serializing it but not the other way round.
/// The `is_reflected` predicate is provided as `self.is_active()`
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReflectIgnoreBehavior {
/// Don't ignore, appear to all systems
#[default]
None,
/// Ignore when serializing but not when reflecting
IgnoreSerialization,
/// Ignore both when serializing and reflecting
IgnoreAlways,
}
impl ReflectIgnoreBehavior {
/// Returns `true` if the ignoring behavior implies member is included in the reflection API, and false otherwise.
pub fn is_active(self) -> bool {
match self {
ReflectIgnoreBehavior::None | ReflectIgnoreBehavior::IgnoreSerialization => true,
ReflectIgnoreBehavior::IgnoreAlways => false,
}
}
/// The exact logical opposite of `self.is_active()` returns true iff this member is not part of the reflection API whatsoever (neither serialized nor reflected)
pub fn is_ignored(self) -> bool {
!self.is_active()
}
}
#[derive(Default, Clone)]
pub(crate) enum CloneBehavior {
#[default]
Default,
Trait,
Func(syn::ExprPath),
}
/// Controls how the default value is determined for a field.
#[derive(Default, Clone)]
pub(crate) enum DefaultBehavior {
/// Field is required.
#[default]
Required,
/// Field can be defaulted using `Default::default()`.
Default,
/// Field can be created using the given function name.
///
/// This assumes the function is in scope, is callable with zero arguments,
/// and returns the expected type.
Func(syn::ExprPath),
}
/// A container for attributes defined on a reflected type's field.
#[derive(Default, Clone)]
pub(crate) struct FieldAttributes {
/// Determines how this field should be ignored if at all.
pub ignore: ReflectIgnoreBehavior,
/// Sets the clone behavior of this field.
pub clone: CloneBehavior,
/// Sets the default behavior of this field.
pub default: DefaultBehavior,
/// Custom attributes created via `#[reflect(@...)]`.
pub custom_attributes: CustomAttributes,
/// For defining the remote wrapper type that should be used in place of the field for reflection logic.
pub remote: Option<Type>,
}
impl FieldAttributes {
/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`).
pub fn parse_attributes(attrs: &[Attribute]) -> syn::Result<Self> {
let mut args = FieldAttributes::default();
attrs
.iter()
.filter_map(|attr| {
if !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME) {
// Not a reflect attribute -> skip
return None;
}
let Meta::List(meta) = &attr.meta else {
return Some(syn::Error::new_spanned(attr, "expected meta list"));
};
// Parse all attributes inside the list, collecting any errors
meta.parse_args_with(terminated_parser(Token![,], |stream| {
args.parse_field_attribute(stream)
}))
.err()
})
.reduce(|mut acc, err| {
acc.combine(err);
acc
})
.map_or(Ok(args), Err)
}
/// Parses a single field attribute.
fn parse_field_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![@]) {
self.parse_custom_attribute(input)
} else if lookahead.peek(kw::ignore) {
self.parse_ignore(input)
} else if lookahead.peek(kw::skip_serializing) {
self.parse_skip_serializing(input)
} else if lookahead.peek(kw::clone) {
self.parse_clone(input)
} else if lookahead.peek(kw::default) {
self.parse_default(input)
} else if lookahead.peek(kw::remote) {
self.parse_remote(input)
} else {
Err(lookahead.error())
}
}
/// Parse `ignore` attribute.
///
/// Examples:
/// - `#[reflect(ignore)]`
fn parse_ignore(&mut self, input: ParseStream) -> syn::Result<()> {
if self.ignore != ReflectIgnoreBehavior::None {
return Err(input.error(format!(
"only one of {:?} is allowed",
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
)));
}
input.parse::<kw::ignore>()?;
self.ignore = ReflectIgnoreBehavior::IgnoreAlways;
Ok(())
}
/// Parse `skip_serializing` attribute.
///
/// Examples:
/// - `#[reflect(skip_serializing)]`
fn parse_skip_serializing(&mut self, input: ParseStream) -> syn::Result<()> {
if self.ignore != ReflectIgnoreBehavior::None {
return Err(input.error(format!(
"only one of {:?} is allowed",
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
)));
}
input.parse::<kw::skip_serializing>()?;
self.ignore = ReflectIgnoreBehavior::IgnoreSerialization;
Ok(())
}
/// Parse `clone` attribute.
///
/// Examples:
/// - `#[reflect(clone)]`
/// - `#[reflect(clone = "path::to::func")]`
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
if !matches!(self.clone, CloneBehavior::Default) {
return Err(input.error(format!("only one of {:?} is allowed", [CLONE_ATTR])));
}
input.parse::<kw::clone>()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let lit = input.parse::<LitStr>()?;
self.clone = CloneBehavior::Func(lit.parse()?);
} else {
self.clone = CloneBehavior::Trait;
}
Ok(())
}
/// Parse `default` attribute.
///
/// Examples:
/// - `#[reflect(default)]`
/// - `#[reflect(default = "path::to::func")]`
fn parse_default(&mut self, input: ParseStream) -> syn::Result<()> {
if !matches!(self.default, DefaultBehavior::Required) {
return Err(input.error(format!("only one of {:?} is allowed", [DEFAULT_ATTR])));
}
input.parse::<kw::default>()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let lit = input.parse::<LitStr>()?;
self.default = DefaultBehavior::Func(lit.parse()?);
} else {
self.default = DefaultBehavior::Default;
}
Ok(())
}
/// Parse `@` (custom attribute) attribute.
///
/// Examples:
/// - `#[reflect(@(foo = "bar"))]`
/// - `#[reflect(@(min = 0.0, max = 1.0))]`
fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
self.custom_attributes.parse_custom_attribute(input)
}
/// Parse `remote` attribute.
///
/// Examples:
/// - `#[reflect(remote = path::to::RemoteType)]`
fn parse_remote(&mut self, input: ParseStream) -> syn::Result<()> {
if let Some(remote) = self.remote.as_ref() {
return Err(input.error(format!(
"remote type already specified as {}",
remote.to_token_stream()
)));
}
input.parse::<kw::remote>()?;
input.parse::<Token![=]>()?;
self.remote = Some(input.parse()?);
Ok(())
}
/// Returns `Some(true)` if the field has a generic remote type.
///
/// If the remote type is not generic, returns `Some(false)`.
///
/// If the field does not have a remote type, returns `None`.
pub fn is_remote_generic(&self) -> Option<bool> {
if let Type::Path(type_path) = self.remote.as_ref()? {
type_path
.path
.segments
.last()
.map(|segment| !segment.arguments.is_empty())
} else {
Some(false)
}
}
}

View File

@@ -0,0 +1,330 @@
use crate::{
container_attributes::REFLECT_DEFAULT,
derive_data::ReflectEnum,
enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},
field_attributes::DefaultBehavior,
ident::ident_or_index,
where_clause_options::WhereClauseOptions,
ReflectMeta, ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};
use proc_macro2::Span;
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 {
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 {
impl_struct_internal(reflect_struct, true)
}
pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
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 = WhereClauseOptions::new(meta).extend_where_clause(where_clause);
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::PartialReflect) -> #FQOption<Self> {
#FQOption::Some(
#FQClone::clone(
<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#type_path #ty_generics>(reflect)?
)
)
}
}
}
}
/// Implements `FromReflect` for the given enum type
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let enum_path = reflect_enum.meta().type_path();
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let ref_value = Ident::new("__param0", Span::call_site());
let EnumVariantOutputData {
variant_names,
variant_constructors,
..
} = FromReflectVariantBuilder::new(reflect_enum).build(&ref_value);
let match_branches = if reflect_enum.meta().is_remote_wrapper() {
quote! {
#(#variant_names => #fqoption::Some(Self(#variant_constructors)),)*
}
} else {
quote! {
#(#variant_names => #fqoption::Some(#variant_constructors),)*
}
};
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 = reflect_enum
.where_clause_options()
.extend_where_clause(where_clause);
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::PartialReflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value)
{
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#match_branches
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
}
} else {
#FQOption::None
}
}
}
}
}
/// Container for a struct's members (field name or index) and their
/// corresponding values.
struct MemberValuePair(Vec<Member>, Vec<proc_macro2::TokenStream>);
impl MemberValuePair {
pub fn new(items: (Vec<Member>, Vec<proc_macro2::TokenStream>)) -> Self {
Self(items.0, items.1)
}
}
fn impl_struct_internal(
reflect_struct: &ReflectStruct,
is_tuple: bool,
) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let struct_path = reflect_struct.meta().type_path();
let remote_ty = reflect_struct.meta().remote_ty();
let bevy_reflect_path = reflect_struct.meta().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 {
Ident::new("Struct", Span::call_site())
};
let MemberValuePair(active_members, active_values) =
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);
// The constructed "Self" ident
let __this = Ident::new("__this", Span::call_site());
// The reflected type: either `Self` or a remote type
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {
let constructor = match remote_ty.as_expr_path() {
Ok(path) => path,
Err(err) => return err.into_compile_error(),
};
let remote_ty = remote_ty.type_path();
(
quote!(#remote_ty),
quote!(#constructor),
quote!(Self(#__this)),
)
} else {
(quote!(Self), quote!(Self), quote!(#__this))
};
let constructor = if is_defaultable {
quote! {
let mut #__this = <#reflect_ty as #FQDefault>::default();
#(
if let #fqoption::Some(__field) = #active_values() {
// Iff field exists -> use its value
#__this.#active_members = __field;
}
)*
#FQOption::Some(#retval)
}
} else {
let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);
quote! {
let #__this = #constructor {
#(#active_members: #active_values()?,)*
#(#ignored_members: #ignored_values,)*
};
#FQOption::Some(#retval)
}
};
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
// Add FromReflect bound for each active field
let where_from_reflect_clause = reflect_struct
.where_clause_options()
.extend_where_clause(where_clause);
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::PartialReflect) -> #FQOption<Self> {
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)
= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)
{
#constructor
} else {
#FQOption::None
}
}
}
}
}
/// Get the collection of ignored field definitions
///
/// Each value of the `MemberValuePair` is a token stream that generates a
/// a default value for the ignored field.
fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {
MemberValuePair::new(
reflect_struct
.ignored_fields()
.map(|field| {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let value = match &field.attrs.default {
DefaultBehavior::Func(path) => quote! {#path()},
_ => quote! {#FQDefault::default()},
};
(member, value)
})
.unzip(),
)
}
/// Get the collection of active field definitions.
///
/// Each value of the `MemberValuePair` is a token stream that generates a
/// closure of type `fn() -> Option<T>` where `T` is that field's type.
fn get_active_fields(
reflect_struct: &ReflectStruct,
dyn_struct_name: &Ident,
struct_type: &Ident,
is_tuple: bool,
) -> MemberValuePair {
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
MemberValuePair::new(
reflect_struct
.active_fields()
.map(|field| {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let accessor = get_field_accessor(
field.data,
field.reflection_index.expect("field should be active"),
is_tuple,
);
let ty = field.reflected_type().clone();
let real_ty = &field.data.ty;
let get_field = quote! {
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
};
let into_remote = |value: proc_macro2::TokenStream| {
if field.attrs.is_remote_generic().unwrap_or_default() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute_copy::<#ty, #real_ty>(
&::core::mem::ManuallyDrop::new(#value?)
)
}
)
}
} else if field.attrs().remote.is_some() {
quote! {
#FQOption::Some(
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
unsafe {
::core::mem::transmute::<#ty, #real_ty>(#value?)
}
)
}
} else {
value
}
};
let value = match &field.attrs.default {
DefaultBehavior::Func(path) => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#path())
}
)
}
}
DefaultBehavior::Default => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
});
quote! {
(||
if let #FQOption::Some(field) = #get_field {
#value
} else {
#FQOption::Some(#FQDefault::default())
}
)
}
}
DefaultBehavior::Required => {
let value = into_remote(quote! {
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)
});
quote! {
(|| #value)
}
}
};
(member, value)
})
.unzip(),
)
}
/// 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())))
}
}

View File

@@ -0,0 +1,72 @@
use crate::derive_data::ReflectMeta;
use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{GenericParam, Token};
/// Creates a `TokenStream` for generating an expression that creates a `Generics` instance.
///
/// Returns `None` if `Generics` cannot or should not be generated.
pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option<TokenStream> {
if !meta.attrs().type_path_attrs().should_auto_derive() {
// Cannot verify that all generic parameters implement `TypePath`
return None;
}
let bevy_reflect_path = meta.bevy_reflect_path();
let generics = meta
.type_path()
.generics()
.params
.iter()
.filter_map(|param| match param {
GenericParam::Type(ty_param) => {
let ident = &ty_param.ident;
let name = ident.to_string();
let with_default = ty_param
.default
.as_ref()
.map(|default_ty| quote!(.with_default::<#default_ty>()));
Some(quote! {
#bevy_reflect_path::GenericInfo::Type(
#bevy_reflect_path::TypeParamInfo::new::<#ident>(
#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name),
)
#with_default
)
})
}
GenericParam::Const(const_param) => {
let ty = &const_param.ty;
let name = const_param.ident.to_string();
let with_default = const_param.default.as_ref().map(|default| {
// We add the `as #ty` to ensure that the correct type is inferred.
quote!(.with_default(#default as #ty))
});
Some(quote! {
#[allow(
clippy::unnecessary_cast,
reason = "reflection requires an explicit type hint for const generics"
)]
#bevy_reflect_path::GenericInfo::Const(
#bevy_reflect_path::ConstParamInfo::new::<#ty>(
#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name),
)
#with_default
)
})
}
GenericParam::Lifetime(_) => None,
})
.collect::<Punctuated<_, Token![,]>>();
if generics.is_empty() {
// No generics to generate
return None;
}
Some(quote!(#bevy_reflect_path::Generics::from_iter([ #generics ])))
}

45
vendor/bevy_reflect_derive/src/ident.rs vendored Normal file
View File

@@ -0,0 +1,45 @@
use proc_macro2::{Ident, Span};
use syn::Member;
/// Returns the "reflected" ident for a given string.
///
/// # Example
///
/// ```
/// # use proc_macro2::Ident;
/// # // We can't import this method because of its visibility.
/// # fn get_reflect_ident(name: &str) -> Ident {
/// # let reflected = format!("Reflect{name}");
/// # Ident::new(&reflected, proc_macro2::Span::call_site())
/// # }
/// let reflected: Ident = get_reflect_ident("Hash");
/// assert_eq!("ReflectHash", reflected.to_string());
/// ```
pub(crate) fn get_reflect_ident(name: &str) -> Ident {
let reflected = format!("Reflect{name}");
Ident::new(&reflected, Span::call_site())
}
/// Returns a [`Member`] made of `ident` or `index` if `ident` is `None`.
///
/// Rust struct syntax allows for `Struct { foo: "string" }` with explicitly
/// named fields. It allows the `Struct { 0: "string" }` syntax when the struct
/// is declared as a tuple struct.
///
/// ```
/// struct Foo { field: &'static str }
/// struct Bar(&'static str);
/// let Foo { field } = Foo { field: "hi" };
/// let Bar { 0: field } = Bar { 0: "hello" };
/// let Bar(field) = Bar("hello"); // more common syntax
/// ```
///
/// This function helps field access in contexts where you are declaring either
/// a tuple struct or a struct with named fields. If you don't have a field name,
/// it means that you must access the field through an index.
pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
ident.map_or_else(
|| Member::Unnamed(index.into()),
|ident| Member::Named(ident.clone()),
)
}

View File

@@ -0,0 +1,13 @@
use crate::{derive_data::ReflectDerive, remote::generate_remote_assertions};
use quote::quote;
/// Generates an anonymous block containing compile-time assertions.
pub(crate) fn impl_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let mut output = quote!();
if let Some(assertions) = generate_remote_assertions(derive_data) {
output.extend(assertions);
}
output
}

View File

@@ -0,0 +1,158 @@
use bevy_macro_utils::fq_std::{FQAny, FQOption, FQResult};
use quote::quote;
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
pub fn impl_full_reflect(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let any_impls = if meta.is_remote_wrapper() {
quote! {
#[inline]
fn into_any(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #FQAny> {
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(self.0)
}
#[inline]
fn as_any(&self) -> &dyn #FQAny {
&self.0
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
&mut self.0
}
}
} else {
quote! {
#[inline]
fn into_any(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #FQAny> {
self
}
#[inline]
fn as_any(&self) -> &dyn #FQAny {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn #FQAny {
self
}
}
};
quote! {
impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause {
#any_impls
#[inline]
fn into_reflect(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect> {
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 set(
&mut self,
value: #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>
) -> #FQResult<(), #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>> {
*self = <dyn #bevy_reflect_path::Reflect>::take(value)?;
#FQResult::Ok(())
}
}
}
}
pub fn common_partial_reflect_methods(
meta: &ReflectMeta,
default_partial_eq_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
default_hash_delegate: impl FnOnce() -> Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let debug_fn = meta.attrs().get_debug_impl();
let partial_eq_fn = meta
.attrs()
.get_partial_eq_impl(bevy_reflect_path)
.or_else(move || {
let default_delegate = default_partial_eq_delegate();
default_delegate.map(|func| {
quote! {
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
(#func)(self, value)
}
}
})
});
let hash_fn = meta
.attrs()
.get_hash_impl(bevy_reflect_path)
.or_else(move || {
let default_delegate = default_hash_delegate();
default_delegate.map(|func| {
quote! {
fn reflect_hash(&self) -> #FQOption<u64> {
(#func)(self)
}
}
})
});
quote! {
#[inline]
fn try_into_reflect(
self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>
) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect>> {
#FQResult::Ok(self)
}
#[inline]
fn try_as_reflect(&self) -> #FQOption<&dyn #bevy_reflect_path::Reflect> {
#FQOption::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self) -> #FQOption<&mut dyn #bevy_reflect_path::Reflect> {
#FQOption::Some(self)
}
#[inline]
fn into_partial_reflect(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self) -> &dyn #bevy_reflect_path::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::PartialReflect {
self
}
#hash_fn
#partial_eq_fn
#debug_fn
}
}

View File

@@ -0,0 +1,439 @@
use crate::{
derive_data::{EnumVariantFields, ReflectEnum, StructField},
enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder},
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
};
use bevy_macro_utils::fq_std::{FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{Fields, Path};
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let enum_path = reflect_enum.meta().type_path();
let is_remote = reflect_enum.meta().is_remote_wrapper();
// For `match self` expressions where self is a reference
let match_this = if is_remote {
quote!(&self.0)
} else {
quote!(self)
};
// For `match self` expressions where self is a mutable reference
let match_this_mut = if is_remote {
quote!(&mut self.0)
} else {
quote!(self)
};
// For `*self` assignments
let deref_this = if is_remote {
quote!(self.0)
} else {
quote!(*self)
};
let ref_name = Ident::new("__name_param", Span::call_site());
let ref_index = Ident::new("__index_param", Span::call_site());
let ref_value = Ident::new("__value_param", Span::call_site());
let where_clause_options = reflect_enum.where_clause_options();
let EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,
enum_variant_name,
enum_variant_index,
enum_variant_type,
} = generate_impls(reflect_enum, &ref_index, &ref_name);
let EnumVariantOutputData {
variant_names,
variant_constructors,
..
} = TryApplyVariantBuilder::new(reflect_enum).build(&ref_value);
let typed_impl = impl_typed(
reflect_enum.meta(),
&where_clause_options,
reflect_enum.to_info_tokens(),
);
let type_path_impl = impl_type_path(reflect_enum.meta());
let full_reflect_impl = impl_full_reflect(reflect_enum.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_enum.meta(),
|| Some(quote!(#bevy_reflect_path::enum_partial_eq)),
|| Some(quote!(#bevy_reflect_path::enum_hash)),
);
let clone_fn = reflect_enum.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_enum.meta(), &where_clause_options);
let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) =
reflect_enum.meta().type_path().generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match #match_this {
#(#enum_field,)*
_ => #FQOption::None,
}
}
fn field_at(&self, #ref_index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match #match_this {
#(#enum_field_at,)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, #ref_name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match #match_this_mut {
#(#enum_field_mut,)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, #ref_index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match #match_this_mut {
#(#enum_field_at_mut,)*
_ => #FQOption::None,
}
}
fn index_of(&self, #ref_name: &str) -> #FQOption<usize> {
match #match_this {
#(#enum_index_of,)*
_ => #FQOption::None,
}
}
fn name_at(&self, #ref_index: usize) -> #FQOption<&str> {
match #match_this {
#(#enum_name_at,)*
_ => #FQOption::None,
}
}
fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter {
#bevy_reflect_path::VariantFieldIter::new(self)
}
#[inline]
fn field_len(&self) -> usize {
match #match_this {
#(#enum_field_len,)*
_ => 0,
}
}
#[inline]
fn variant_name(&self) -> &str {
match #match_this {
#(#enum_variant_name,)*
_ => unreachable!(),
}
}
#[inline]
fn variant_index(&self) -> usize {
match #match_this {
#(#enum_variant_index,)*
_ => unreachable!(),
}
}
#[inline]
fn variant_type(&self) -> #bevy_reflect_path::VariantType {
match #match_this {
#(#enum_variant_type,)*
_ => unreachable!(),
}
}
fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum {
#bevy_reflect_path::DynamicEnum::from_ref::<Self>(self)
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #enum_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
#ref_value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value) {
if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {
// Same variant -> just update fields
match #bevy_reflect_path::Enum::variant_type(#ref_value) {
#bevy_reflect_path::VariantType::Struct => {
for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {
let name = field.name().unwrap();
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
}
}
}
#bevy_reflect_path::VariantType::Tuple => {
for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
}
}
}
_ => {}
}
} else {
// New variant -> perform a switch
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
#(#variant_names => {
#deref_this = #variant_constructors
})*
name => {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::UnknownVariant {
enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),
variant_name: ::core::convert::Into::into(name),
}
);
}
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(#ref_value),
to_kind: #bevy_reflect_path::ReflectKind::Enum,
}
);
}
#FQResult::Ok(())
}
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Enum
}
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Enum(self)
}
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Enum(self)
}
#common_methods
#clone_fn
}
}
}
struct EnumImpls {
enum_field: Vec<proc_macro2::TokenStream>,
enum_field_mut: Vec<proc_macro2::TokenStream>,
enum_field_at: Vec<proc_macro2::TokenStream>,
enum_field_at_mut: Vec<proc_macro2::TokenStream>,
enum_index_of: Vec<proc_macro2::TokenStream>,
enum_name_at: Vec<proc_macro2::TokenStream>,
enum_field_len: Vec<proc_macro2::TokenStream>,
enum_variant_name: Vec<proc_macro2::TokenStream>,
enum_variant_index: Vec<proc_macro2::TokenStream>,
enum_variant_type: Vec<proc_macro2::TokenStream>,
}
fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let mut enum_field = Vec::new();
let mut enum_field_mut = Vec::new();
let mut enum_field_at = Vec::new();
let mut enum_field_at_mut = Vec::new();
let mut enum_index_of = Vec::new();
let mut enum_name_at = Vec::new();
let mut enum_field_len = Vec::new();
let mut enum_variant_name = Vec::new();
let mut enum_variant_index = Vec::new();
let mut enum_variant_type = Vec::new();
for (variant_index, variant) in reflect_enum.variants().iter().enumerate() {
let ident = &variant.data.ident;
let name = ident.to_string();
let unit = reflect_enum.get_unit(ident);
let variant_type_ident = match variant.data.fields {
Fields::Unit => Ident::new("Unit", Span::call_site()),
Fields::Unnamed(..) => Ident::new("Tuple", Span::call_site()),
Fields::Named(..) => Ident::new("Struct", Span::call_site()),
};
enum_variant_name.push(quote! {
#unit{..} => #name
});
enum_variant_index.push(quote! {
#unit{..} => #variant_index
});
enum_variant_type.push(quote! {
#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident
});
fn process_fields(
fields: &[StructField],
mut f: impl FnMut(&StructField) + Sized,
) -> usize {
let mut field_len = 0;
for field in fields.iter() {
if field.attrs.ignore.is_ignored() {
// Ignored field
continue;
};
f(field);
field_len += 1;
}
field_len
}
/// Process the field value to account for remote types.
///
/// If the field is a remote type, then the value will be transmuted accordingly.
fn process_field_value(
ident: &Ident,
field: &StructField,
is_mutable: bool,
bevy_reflect_path: &Path,
) -> proc_macro2::TokenStream {
let method = if is_mutable {
quote!(as_wrapper_mut)
} else {
quote!(as_wrapper)
};
field
.attrs
.remote
.as_ref()
.map(|ty| quote!(<#ty as #bevy_reflect_path::ReflectRemote>::#method(#ident)))
.unwrap_or_else(|| quote!(#ident))
}
match &variant.fields {
EnumVariantFields::Unit => {
let field_len = process_fields(&[], |_| {});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
EnumVariantFields::Unnamed(fields) => {
let field_len = process_fields(fields, |field: &StructField| {
let reflection_index = field
.reflection_index
.expect("reflection index should exist for active field");
let declare_field = syn::Index::from(field.declaration_index);
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field_at.push(quote! {
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
EnumVariantFields::Named(fields) => {
let field_len = process_fields(fields, |field: &StructField| {
let field_ident = field.data.ident.as_ref().unwrap();
let field_name = field_ident.to_string();
let reflection_index = field
.reflection_index
.expect("reflection index should exist for active field");
let __value = Ident::new("__value", Span::call_site());
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
enum_field.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_ref)
});
enum_field_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_mut)
});
enum_field_at.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
});
enum_field_at_mut.push(quote! {
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
});
enum_index_of.push(quote! {
#unit{ .. } if #ref_name == #field_name => #FQOption::Some(#reflection_index)
});
enum_name_at.push(quote! {
#unit{ .. } if #ref_index == #reflection_index => #FQOption::Some(#field_name)
});
});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
}
};
}
EnumImpls {
enum_field,
enum_field_mut,
enum_field_at,
enum_field_at_mut,
enum_index_of,
enum_name_at,
enum_field_len,
enum_variant_name,
enum_variant_index,
enum_variant_type,
}
}

View File

@@ -0,0 +1,37 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use bevy_macro_utils::fq_std::FQResult;
use quote::quote;
pub(crate) fn impl_from_arg(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::args::FromArg for #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_owned()
}
}
impl #impl_generics #bevy_reflect::func::args::FromArg for &'static #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = &'from_arg #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_ref()
}
}
impl #impl_generics #bevy_reflect::func::args::FromArg for &'static mut #type_path #ty_generics #where_reflect_clause {
type This<'from_arg> = &'from_arg mut #type_path #ty_generics;
fn from_arg(arg: #bevy_reflect::func::args::Arg) -> #FQResult<Self::This<'_>, #bevy_reflect::func::args::ArgError> {
arg.take_mut()
}
}
}
}

View File

@@ -0,0 +1,25 @@
use crate::{
derive_data::ReflectMeta,
impls::func::{
from_arg::impl_from_arg, get_ownership::impl_get_ownership, into_return::impl_into_return,
},
where_clause_options::WhereClauseOptions,
};
use quote::quote;
pub(crate) fn impl_function_traits(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let get_ownership = impl_get_ownership(meta, where_clause_options);
let from_arg = impl_from_arg(meta, where_clause_options);
let into_return = impl_into_return(meta, where_clause_options);
quote! {
#get_ownership
#from_arg
#into_return
}
}

View File

@@ -0,0 +1,33 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use quote::quote;
pub(crate) fn impl_get_ownership(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::args::GetOwnership for #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Owned
}
}
impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Ref
}
}
impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ mut #type_path #ty_generics #where_reflect_clause {
fn ownership() -> #bevy_reflect::func::args::Ownership {
#bevy_reflect::func::args::Ownership::Mut
}
}
}
}

View File

@@ -0,0 +1,33 @@
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
use quote::quote;
pub(crate) fn impl_into_return(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
let bevy_reflect = meta.bevy_reflect_path();
let type_path = meta.type_path();
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect::func::IntoReturn for #type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Owned(#bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
}
}
impl #impl_generics #bevy_reflect::func::IntoReturn for &#type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Ref(self)
}
}
impl #impl_generics #bevy_reflect::func::IntoReturn for &mut #type_path #ty_generics #where_reflect_clause {
fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return {
#bevy_reflect::func::Return::Mut(self)
}
}
}
}

View File

@@ -0,0 +1,6 @@
pub(crate) use function_impls::impl_function_traits;
mod from_arg;
mod function_impls;
mod get_ownership;
mod into_return;

View File

@@ -0,0 +1,19 @@
mod assertions;
mod common;
mod enums;
#[cfg(feature = "functions")]
mod func;
mod opaque;
mod structs;
mod tuple_structs;
mod typed;
pub(crate) use assertions::impl_assertions;
pub(crate) use common::{common_partial_reflect_methods, impl_full_reflect};
pub(crate) use enums::impl_enum;
#[cfg(feature = "functions")]
pub(crate) use func::impl_function_traits;
pub(crate) use opaque::impl_opaque;
pub(crate) use structs::impl_struct;
pub(crate) use tuple_structs::impl_tuple_struct;
pub(crate) use typed::{impl_type_path, impl_typed};

View File

@@ -0,0 +1,125 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
where_clause_options::WhereClauseOptions,
ReflectMeta,
};
use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
use quote::quote;
/// Implements `GetTypeRegistration` and `Reflect` for the given type data.
pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
let type_path = meta.type_path();
#[cfg(feature = "documentation")]
let with_docs = {
let doc = quote::ToTokens::to_token_stream(meta.doc());
Some(quote!(.with_docs(#doc)))
};
#[cfg(not(feature = "documentation"))]
let with_docs: Option<proc_macro2::TokenStream> = None;
let where_clause_options = WhereClauseOptions::new(meta);
let typed_impl = impl_typed(
meta,
&where_clause_options,
quote! {
let info = #bevy_reflect_path::OpaqueInfo::new::<Self>() #with_docs;
#bevy_reflect_path::TypeInfo::Opaque(info)
},
);
let type_path_impl = impl_type_path(meta);
let full_reflect_impl = impl_full_reflect(meta, &where_clause_options);
let common_methods = common_partial_reflect_methods(meta, || None, || None);
let clone_fn = meta.attrs().get_clone_impl(bevy_reflect_path);
let apply_impl = if let Some(remote_ty) = meta.remote_ty() {
let ty = remote_ty.type_path();
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#ty>(value) {
*self = Self(#FQClone::clone(value));
return #FQResult::Ok(());
}
}
} else {
quote! {
if let #FQOption::Some(value) = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value) {
*self = #FQClone::clone(value);
return #FQResult::Ok(());
}
}
};
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls = crate::impls::impl_function_traits(meta, &where_clause_options);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let get_type_registration_impl = meta.get_type_registration(&where_clause_options);
quote! {
#get_type_registration_impl
#type_path_impl
#typed_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::PartialReflect for #type_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn to_dynamic(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self))
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
#apply_impl
#FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(value)),
to_type: ::core::convert::Into::into(<Self as #bevy_reflect_path::TypePath>::type_path()),
}
)
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Opaque
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Opaque(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Opaque(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Opaque(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,182 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
struct_utility::FieldAccessors,
ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data.
pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_path = reflect_struct.meta().type_path();
let field_names = reflect_struct
.active_fields()
.map(|field| {
field
.data
.ident
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| field.declaration_index.to_string())
})
.collect::<Vec<String>>();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let typed_impl = impl_typed(
reflect_struct.meta(),
&where_clause_options,
reflect_struct.to_info_tokens(false),
);
let type_path_impl = impl_type_path(reflect_struct.meta());
let full_reflect_impl = impl_full_reflect(reflect_struct.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::struct_partial_eq)),
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match name {
#(#field_names => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
fn field_at(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_at_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
fn name_at(&self, index: usize) -> #FQOption<&str> {
match index {
#(#field_indices => #fqoption::Some(#field_names),)*
_ => #FQOption::None,
}
}
fn field_len(&self) -> usize {
#field_count
}
fn iter_fields(&self) -> #bevy_reflect_path::FieldIter {
#bevy_reflect_path::FieldIter::new(self)
}
fn to_dynamic_struct(&self) -> #bevy_reflect_path::DynamicStruct {
let mut dynamic: #bevy_reflect_path::DynamicStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
dynamic
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::Struct(struct_value)
= #bevy_reflect_path::PartialReflect::reflect_ref(value) {
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Struct::iter_fields(struct_value)) {
let name = #bevy_reflect_path::Struct::name_at(struct_value, i).unwrap();
if let #FQOption::Some(v) = #bevy_reflect_path::Struct::field_mut(self, name) {
#bevy_reflect_path::PartialReflect::try_apply(v, value)?;
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(value),
to_kind: #bevy_reflect_path::ReflectKind::Struct
}
);
}
#FQResult::Ok(())
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::Struct
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::Struct(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::Struct(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::Struct(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,148 @@
use crate::{
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
struct_utility::FieldAccessors,
ReflectStruct,
};
use bevy_macro_utils::fq_std::{FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data.
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
let fqoption = FQOption.into_token_stream();
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
let struct_path = reflect_struct.meta().type_path();
let FieldAccessors {
fields_ref,
fields_mut,
field_indices,
field_count,
..
} = FieldAccessors::new(reflect_struct);
let where_clause_options = reflect_struct.where_clause_options();
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);
let typed_impl = impl_typed(
reflect_struct.meta(),
&where_clause_options,
reflect_struct.to_info_tokens(true),
);
let type_path_impl = impl_type_path(reflect_struct.meta());
let full_reflect_impl = impl_full_reflect(reflect_struct.meta(), &where_clause_options);
let common_methods = common_partial_reflect_methods(
reflect_struct.meta(),
|| Some(quote!(#bevy_reflect_path::tuple_struct_partial_eq)),
|| None,
);
let clone_fn = reflect_struct.get_clone_impl();
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()
.type_path()
.generics()
.split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#get_type_registration_impl
#typed_impl
#type_path_impl
#full_reflect_impl
#function_impls
impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause {
fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_ref),)*
_ => #FQOption::None,
}
}
fn field_mut(&mut self, index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
match index {
#(#field_indices => #fqoption::Some(#fields_mut),)*
_ => #FQOption::None,
}
}
#[inline]
fn field_len(&self) -> usize {
#field_count
}
#[inline]
fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter {
#bevy_reflect_path::TupleStructFieldIter::new(self)
}
fn to_dynamic_tuple_struct(&self) -> #bevy_reflect_path::DynamicTupleStruct {
let mut dynamic: #bevy_reflect_path::DynamicTupleStruct = #FQDefault::default();
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
dynamic
}
}
impl #impl_generics #bevy_reflect_path::PartialReflect for #struct_path #ty_generics #where_reflect_clause {
#[inline]
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
}
#[inline]
fn try_apply(
&mut self,
value: &dyn #bevy_reflect_path::PartialReflect
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) =
#bevy_reflect_path::PartialReflect::reflect_ref(value) {
for (i, value) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::TupleStruct::iter_fields(struct_value)) {
if let #FQOption::Some(v) = #bevy_reflect_path::TupleStruct::field_mut(self, i) {
#bevy_reflect_path::PartialReflect::try_apply(v, value)?;
}
}
} else {
return #FQResult::Err(
#bevy_reflect_path::ApplyError::MismatchedKinds {
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(value),
to_kind: #bevy_reflect_path::ReflectKind::TupleStruct,
}
);
}
#FQResult::Ok(())
}
#[inline]
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
#bevy_reflect_path::ReflectKind::TupleStruct
}
#[inline]
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
#bevy_reflect_path::ReflectRef::TupleStruct(self)
}
#[inline]
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
#bevy_reflect_path::ReflectMut::TupleStruct(self)
}
#[inline]
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
#bevy_reflect_path::ReflectOwned::TupleStruct(self)
}
#common_methods
#clone_fn
}
}
}

View File

@@ -0,0 +1,174 @@
use crate::{
derive_data::{ReflectMeta, ReflectTypePath},
string_expr::StringExpr,
where_clause_options::WhereClauseOptions,
};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell` to generate `'static` references.
fn static_type_cell(
meta: &ReflectMeta,
property: TypedProperty,
generator: TokenStream,
) -> TokenStream {
let bevy_reflect_path = meta.bevy_reflect_path();
if meta.type_path().impl_is_generic() {
let cell_type = match property {
TypedProperty::TypePath => quote!(GenericTypePathCell),
TypedProperty::TypeInfo => quote!(GenericTypeInfoCell),
};
quote! {
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_insert::<Self, _>(|| {
#generator
})
}
} else {
let cell_type = match property {
TypedProperty::TypePath => unreachable!(
"Cannot have a non-generic type path cell. Use string literals and core::concat instead."
),
TypedProperty::TypeInfo => quote!(NonGenericTypeInfoCell),
};
quote! {
static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new();
CELL.get_or_set(|| {
#generator
})
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum TypedProperty {
TypeInfo,
TypePath,
}
pub(crate) fn impl_type_path(meta: &ReflectMeta) -> TokenStream {
let where_clause_options = WhereClauseOptions::new(meta);
if !meta.attrs().type_path_attrs().should_auto_derive() {
return TokenStream::new();
}
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let (long_type_path, short_type_path) = if type_path.impl_is_generic() {
let long_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.long_type_path(bevy_reflect_path).into_owned(),
);
let short_path_cell = static_type_cell(
meta,
TypedProperty::TypePath,
type_path.short_type_path(bevy_reflect_path).into_owned(),
);
(
long_path_cell.to_token_stream(),
short_path_cell.to_token_stream(),
)
} else {
(
type_path.long_type_path(bevy_reflect_path).into_borrowed(),
type_path.short_type_path(bevy_reflect_path).into_borrowed(),
)
};
let type_ident = wrap_in_option(type_path.type_ident().map(StringExpr::into_borrowed));
let module_path = wrap_in_option(type_path.module_path().map(StringExpr::into_borrowed));
let crate_name = wrap_in_option(type_path.crate_name().map(StringExpr::into_borrowed));
let primitive_assert = if let ReflectTypePath::Primitive(_) = type_path {
Some(quote! {
const _: () = {
mod private_scope {
// Compiles if it can be named when there are no imports.
type AssertIsPrimitive = #type_path;
}
};
})
} else {
None
};
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
// Add Typed bound for each active field
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
#primitive_assert
// To ensure alloc is available, but also prevent its name from clashing, we place the implementation inside an anonymous constant
const _: () = {
extern crate alloc;
use alloc::string::ToString;
impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause {
fn type_path() -> &'static str {
#long_type_path
}
fn short_type_path() -> &'static str {
#short_type_path
}
fn type_ident() -> Option<&'static str> {
#type_ident
}
fn crate_name() -> Option<&'static str> {
#crate_name
}
fn module_path() -> Option<&'static str> {
#module_path
}
}
};
}
}
pub(crate) fn impl_typed(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
type_info_generator: TokenStream,
) -> TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator);
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
quote! {
impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause {
#[inline]
fn type_info() -> &'static #bevy_reflect_path::TypeInfo {
#type_info_cell
}
}
}
}
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
fn wrap_in_option(tokens: Option<TokenStream>) -> TokenStream {
match tokens {
Some(tokens) => quote! {
#FQOption::Some(#tokens)
},
None => quote! {
#FQOption::None
},
}
}

845
vendor/bevy_reflect_derive/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,845 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! 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
//! [`TypePath`], as well as the [`reflect_trait`] attribute macro.
//!
//! [`Reflect`]: crate::derive_reflect
//! [`FromReflect`]: crate::derive_from_reflect
//! [`TypePath`]: crate::derive_type_path
//! [`reflect_trait`]: macro@reflect_trait
extern crate proc_macro;
mod attribute_parser;
mod container_attributes;
mod custom_attributes;
mod derive_data;
#[cfg(feature = "documentation")]
mod documentation;
mod enum_utility;
mod field_attributes;
mod from_reflect;
mod generics;
mod ident;
mod impls;
mod meta;
mod reflect_opaque;
mod registration;
mod remote;
mod result_sifter;
mod serialization;
mod string_expr;
mod struct_utility;
mod trait_reflection;
mod type_path;
mod where_clause_options;
use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
use container_attributes::ContainerAttributes;
use derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl, ReflectTypePath};
use proc_macro::TokenStream;
use quote::quote;
use reflect_opaque::ReflectOpaqueDef;
use syn::{parse_macro_input, DeriveInput};
use type_path::NamedTypePathDef;
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
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`].
///
/// [`impl_reflect`]: macro@impl_reflect
/// [`derive_reflect`]: 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 assertions = impls::impl_assertions(&derive_data);
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::Opaque(meta) => (
impls::impl_opaque(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_opaque(&meta))
} else {
None
},
),
};
TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
#assertions
};
})
}
/// 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).
/// It will automatically generate implementations for `Reflect`, `Typed`, `GetTypeRegistration`, and `FromReflect`.
/// And, depending on the item's structure, will either implement `Struct`, `TupleStruct`, or `Enum`.
///
/// See the [`FromReflect`] derive macro for more information on how to customize the `FromReflect` implementation.
///
/// # Container Attributes
///
/// This macro comes with some helper attributes that can be added to the container item
/// in order to provide additional functionality or alter the generated implementations.
///
/// In addition to those listed, this macro can also use the attributes for [`TypePath`] derives.
///
/// ## `#[reflect(Ident)]`
///
/// The `#[reflect(Ident)]` attribute is used to add type data registrations to the `GetTypeRegistration`
/// implementation corresponding to the given identifier, prepended by `Reflect`.
///
/// For example, `#[reflect(Foo, Bar)]` would add two registrations:
/// one for `ReflectFoo` and another for `ReflectBar`.
/// This assumes these types are indeed in-scope wherever this macro is called.
///
/// This is often used with traits that have been marked by the [`#[reflect_trait]`](macro@reflect_trait)
/// macro in order to register the type's implementation of that trait.
///
/// ### Default Registrations
///
/// The following types are automatically registered when deriving `Reflect`:
///
/// * `ReflectFromReflect` (unless opting out of `FromReflect`)
/// * `SerializationData`
/// * `ReflectFromPtr`
///
/// ### Special Identifiers
///
/// There are a few "special" identifiers that work a bit differently:
///
/// * `#[reflect(Clone)]` will force the implementation of `Reflect::reflect_clone` to rely on
/// the type's [`Clone`] implementation.
/// A custom implementation may be provided using `#[reflect(Clone(my_clone_func))]` where
/// `my_clone_func` is the path to a function matching the signature:
/// `(&Self) -> Self`.
/// * `#[reflect(Debug)]` will force the implementation of `Reflect::reflect_debug` to rely on
/// the type's [`Debug`] implementation.
/// A custom implementation may be provided using `#[reflect(Debug(my_debug_func))]` where
/// `my_debug_func` is the path to a function matching the signature:
/// `(&Self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result`.
/// * `#[reflect(PartialEq)]` will force the implementation of `Reflect::reflect_partial_eq` to rely on
/// the type's [`PartialEq`] implementation.
/// A custom implementation may be provided using `#[reflect(PartialEq(my_partial_eq_func))]` where
/// `my_partial_eq_func` is the path to a function matching the signature:
/// `(&Self, value: &dyn #bevy_reflect_path::Reflect) -> bool`.
/// * `#[reflect(Hash)]` will force the implementation of `Reflect::reflect_hash` to rely on
/// the type's [`Hash`] implementation.
/// A custom implementation may be provided using `#[reflect(Hash(my_hash_func))]` where
/// `my_hash_func` is the path to a function matching the signature: `(&Self) -> u64`.
/// * `#[reflect(Default)]` will register the `ReflectDefault` type data as normal.
/// However, it will also affect how certain other operations are performed in order
/// to improve performance and/or robustness.
/// An example of where this is used is in the [`FromReflect`] derive macro,
/// where adding this attribute will cause the `FromReflect` implementation to create
/// a base value using its [`Default`] implementation avoiding issues with ignored fields
/// (for structs and tuple structs only).
///
/// ## `#[reflect(opaque)]`
///
/// The `#[reflect(opaque)]` attribute denotes that the item should implement `Reflect` as an opaque type,
/// hiding its structure and fields from the reflection API.
/// This means that it will forgo implementing `Struct`, `TupleStruct`, or `Enum`.
///
/// Furthermore, it requires that the type implements [`Clone`].
/// If planning to serialize this type using the reflection serializers,
/// then the `Serialize` and `Deserialize` traits will need to be implemented and registered as well.
///
/// ## `#[reflect(from_reflect = false)]`
///
/// This attribute will opt-out of the default `FromReflect` implementation.
///
/// This is useful for when a type can't or shouldn't implement `FromReflect`,
/// or if a manual implementation is desired.
///
/// Note that in the latter case, `ReflectFromReflect` will no longer be automatically registered.
///
/// ## `#[reflect(type_path = false)]`
///
/// This attribute will opt-out of the default `TypePath` implementation.
///
/// This is useful for when a type can't or shouldn't implement `TypePath`,
/// or if a manual implementation is desired.
///
/// ## `#[reflect(no_field_bounds)]`
///
/// This attribute will opt-out of the default trait bounds added to all field types
/// for the generated reflection trait impls.
///
/// Normally, all fields will have the bounds `TypePath`, and either `FromReflect` or `Reflect`
/// depending on if `#[reflect(from_reflect = false)]` is used.
/// However, this might not always be desirable, and so this attribute may be used to remove those bounds.
///
/// ### Example
///
/// If a type is recursive the default bounds will cause an overflow error when building:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)] // ERROR: overflow evaluating the requirement `Foo: FromReflect`
/// struct Foo {
/// foo: Vec<Foo>,
/// }
///
/// // Generates a where clause like:
/// // impl bevy_reflect::Reflect for Foo
/// // where
/// // Self: Any + Send + Sync,
/// // Vec<Foo>: FromReflect + TypePath,
/// ```
///
/// In this case, `Foo` is given the bounds `Vec<Foo>: FromReflect + TypePath`,
/// which requires that `Foo` implements `FromReflect`,
/// which requires that `Vec<Foo>` implements `FromReflect`,
/// and so on, resulting in the error.
///
/// To fix this, we can add `#[reflect(no_field_bounds)]` to `Foo` to remove the bounds on `Vec<Foo>`:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)]
/// #[reflect(no_field_bounds)]
/// struct Foo {
/// foo: Vec<Foo>,
/// }
///
/// // Generates a where clause like:
/// // impl bevy_reflect::Reflect for Foo
/// // where
/// // Self: Any + Send + Sync,
/// ```
///
/// ## `#[reflect(where T: Trait, U::Assoc: Trait, ...)]`
///
/// This attribute can be used to add additional bounds to the generated reflection trait impls.
///
/// This is useful for when a type needs certain bounds only applied to the reflection impls
/// that are not otherwise automatically added by the derive macro.
///
/// ### Example
///
/// In the example below, we want to enforce that `T::Assoc: List` is required in order for
/// `Foo<T>` to be reflectable, but we don't want it to prevent `Foo<T>` from being used
/// in places where `T::Assoc: List` is not required.
///
/// ```ignore
/// trait Trait {
/// type Assoc;
/// }
///
/// #[derive(Reflect)]
/// #[reflect(where T::Assoc: List)]
/// struct Foo<T: Trait> where T::Assoc: Default {
/// value: T::Assoc,
/// }
///
/// // Generates a where clause like:
/// //
/// // impl<T: Trait> bevy_reflect::Reflect for Foo<T>
/// // where
/// // Self: Any + Send + Sync,
/// // T::Assoc: Default,
/// // T: TypePath,
/// // T::Assoc: FromReflect + TypePath,
/// // T::Assoc: List,
/// // {/* ... */}
/// ```
///
/// ## `#[reflect(@...)]`
///
/// This attribute can be used to register custom attributes to the type's `TypeInfo`.
///
/// It accepts any expression after the `@` symbol that resolves to a value which implements `Reflect`.
///
/// Any number of custom attributes may be registered, however, each the type of each attribute must be unique.
/// If two attributes of the same type are registered, the last one will overwrite the first.
///
/// ### Example
///
/// ```ignore
/// #[derive(Reflect)]
/// struct Required;
///
/// #[derive(Reflect)]
/// struct EditorTooltip(String);
///
/// impl EditorTooltip {
/// fn new(text: &str) -> Self {
/// Self(text.to_string())
/// }
/// }
///
/// #[derive(Reflect)]
/// // Specify a "required" status and tooltip:
/// #[reflect(@Required, @EditorTooltip::new("An ID is required!"))]
/// struct Id(u8);
/// ```
///
/// # Field Attributes
///
/// Along with the container attributes, this macro comes with some attributes that may be applied
/// to the contained fields themselves.
///
/// ## `#[reflect(ignore)]`
///
/// This attribute simply marks a field to be ignored by the reflection API.
///
/// This allows fields to completely opt-out of reflection,
/// which may be useful for maintaining invariants, keeping certain data private,
/// or allowing the use of types that do not implement `Reflect` within the container.
///
/// ## `#[reflect(skip_serializing)]`
///
/// This works similar to `#[reflect(ignore)]`, but rather than opting out of _all_ of reflection,
/// it simply opts the field out of both serialization and deserialization.
/// This can be useful when a field should be accessible via reflection, but may not make
/// sense in a serialized form, such as computed data.
///
/// What this does is register the `SerializationData` type within the `GetTypeRegistration` implementation,
/// which will be used by the reflection serializers to determine whether or not the field is serializable.
///
/// ## `#[reflect(clone)]`
///
/// This attribute affects the `Reflect::reflect_clone` implementation.
///
/// Without this attribute, the implementation will rely on the field's own `Reflect::reflect_clone` implementation.
/// When this attribute is present, the implementation will instead use the field's `Clone` implementation directly.
///
/// The attribute may also take the path to a custom function like `#[reflect(clone = "path::to::my_clone_func")]`,
/// where `my_clone_func` matches the signature `(&Self) -> Self`.
///
/// This attribute does nothing if the containing struct/enum has the `#[reflect(Clone)]` attribute.
///
/// ## `#[reflect(@...)]`
///
/// This attribute can be used to register custom attributes to the field's `TypeInfo`.
///
/// It accepts any expression after the `@` symbol that resolves to a value which implements `Reflect`.
///
/// Any number of custom attributes may be registered, however, each the type of each attribute must be unique.
/// If two attributes of the same type are registered, the last one will overwrite the first.
///
/// ### Example
///
/// ```ignore
/// #[derive(Reflect)]
/// struct EditorTooltip(String);
///
/// impl EditorTooltip {
/// fn new(text: &str) -> Self {
/// Self(text.to_string())
/// }
/// }
///
/// #[derive(Reflect)]
/// struct Slider {
/// // Specify a custom range and tooltip:
/// #[reflect(@0.0..=1.0, @EditorTooltip::new("Must be between 0 and 1"))]
/// value: f32,
/// }
/// ```
///
/// [`reflect_trait`]: macro@reflect_trait
#[proc_macro_derive(Reflect, attributes(reflect, type_path, type_name))]
pub fn derive_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
match_reflect_impls(ast, ReflectImplSource::DeriveLocalType)
}
/// Derives the `FromReflect` trait.
///
/// # Field Attributes
///
/// ## `#[reflect(ignore)]`
///
/// The `#[reflect(ignore)]` attribute is shared with the [`#[derive(Reflect)]`](Reflect) macro and has much of the same
/// functionality in that it denotes that a field will be ignored by the reflection API.
///
/// The only major difference is that using it with this derive requires that the field implements [`Default`].
/// Without this requirement, there would be no way for `FromReflect` to automatically construct missing fields
/// that have been ignored.
///
/// ## `#[reflect(default)]`
///
/// If a field cannot be read, this attribute specifies a default value to be used in its place.
///
/// By default, this attribute denotes that the field's type implements [`Default`].
/// However, it can also take in a path string to a user-defined function that will return the default value.
/// This takes the form: `#[reflect(default = "path::to::my_function")]` where `my_function` is a parameterless
/// function that must return some default value for the type.
///
/// Specifying a custom default can be used to give different fields their own specialized defaults,
/// or to remove the `Default` requirement on fields marked with `#[reflect(ignore)]`.
/// Additionally, either form of this attribute can be used to fill in fields that are simply missing,
/// such as when converting a partially-constructed dynamic type to a concrete one.
#[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 ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::FromReflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
let from_reflect_impl = match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => {
from_reflect::impl_struct(&struct_data)
}
ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data),
ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta),
ReflectDerive::Opaque(meta) => from_reflect::impl_opaque(&meta),
};
TokenStream::from(quote! {
const _: () = {
#from_reflect_impl
};
})
}
/// Derives the `TypePath` trait, providing a stable alternative to [`std::any::type_name`].
///
/// # Container Attributes
///
/// ## `#[type_path = "my_crate::foo"]`
///
/// Optionally specifies a custom module path to use instead of [`module_path`].
///
/// This path does not include the final identifier.
///
/// ## `#[type_name = "RenamedType"]`
///
/// Optionally specifies a new terminating identifier for `TypePath`.
///
/// To use this attribute, `#[type_path = "..."]` must also be specified.
#[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,
ReflectProvenance {
source: ReflectImplSource::DeriveLocalType,
trait_: ReflectTraitToImpl::TypePath,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
let type_path_impl = impls::impl_type_path(derive_data.meta());
TokenStream::from(quote! {
const _: () = {
#type_path_impl
};
})
}
/// A macro that automatically generates type data for traits, which their implementors can then register.
///
/// The output of this macro is a struct that takes reflected instances of the implementor's type
/// and returns the value as a trait object.
/// Because of this, **it can only be used on [object-safe] traits.**
///
/// For a trait named `MyTrait`, this will generate the struct `ReflectMyTrait`.
/// The generated struct can be created using `FromType` with any type that implements the trait.
/// The creation and registration of this generated struct as type data can be automatically handled
/// by [`#[derive(Reflect)]`](Reflect).
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// # use std::any::TypeId;
/// # use bevy_reflect_derive::{Reflect, reflect_trait};
/// #[reflect_trait] // Generates `ReflectMyTrait`
/// trait MyTrait {
/// fn print(&self) -> &str;
/// }
///
/// #[derive(Reflect)]
/// #[reflect(MyTrait)] // Automatically registers `ReflectMyTrait`
/// struct SomeStruct;
///
/// impl MyTrait for SomeStruct {
/// fn print(&self) -> &str {
/// "Hello, World!"
/// }
/// }
///
/// // We can create the type data manually if we wanted:
/// let my_trait: ReflectMyTrait = FromType::<SomeStruct>::from_type();
///
/// // Or we can simply get it from the registry:
/// let mut registry = TypeRegistry::default();
/// registry.register::<SomeStruct>();
/// let my_trait = registry
/// .get_type_data::<ReflectMyTrait>(TypeId::of::<SomeStruct>())
/// .unwrap();
///
/// // Then use it on reflected data
/// let reflected: Box<dyn Reflect> = Box::new(SomeStruct);
/// let reflected_my_trait: &dyn MyTrait = my_trait.get(&*reflected).unwrap();
/// assert_eq!("Hello, World!", reflected_my_trait.print());
/// ```
///
/// [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
#[proc_macro_attribute]
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
trait_reflection::reflect_trait(&args, input)
}
/// Generates a wrapper type that can be used to "derive `Reflect`" for remote types.
///
/// This works by wrapping the remote type in a generated wrapper that has the `#[repr(transparent)]` attribute.
/// This allows the two types to be safely [transmuted] back-and-forth.
///
/// # Defining the Wrapper
///
/// Before defining the wrapper type, please note that it is _required_ that all fields of the remote type are public.
/// The generated code will, at times, need to access or mutate them,
/// and we do not currently have a way to assign getters/setters to each field
/// (but this may change in the future).
///
/// The wrapper definition should match the remote type 1-to-1.
/// This includes the naming and ordering of the fields and variants.
///
/// Generics and lifetimes do _not_ need to have the same names, however, they _do_ need to follow the same order.
/// Additionally, whether generics are inlined or placed in a where clause should not matter.
///
/// Lastly, all macros and doc-comments should be placed __below__ this attribute.
/// If they are placed above, they will not be properly passed to the generated wrapper type.
///
/// # Example
///
/// Given a remote type, `RemoteType`:
///
/// ```
/// #[derive(Default)]
/// struct RemoteType<T>
/// where
/// T: Default + Clone,
/// {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// We would define our wrapper type as such:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[reflect_remote(RemoteType<T>)]
/// #[derive(Default)]
/// pub struct WrapperType<T: Default + Clone> {
/// pub foo: T,
/// pub bar: usize
/// }
/// ```
///
/// Apart from all the reflection trait implementations, this generates something like the following:
///
/// ```ignore
/// use external_crate::RemoteType;
///
/// #[derive(Default)]
/// #[repr(transparent)]
/// pub struct Wrapper<T: Default + Clone>(RemoteType<T>);
/// ```
///
/// # Usage as a Field
///
/// You can tell `Reflect` to use a remote type's wrapper internally on fields of a struct or enum.
/// This allows the real type to be used as usual while `Reflect` handles everything internally.
/// To do this, add the `#[reflect(remote = path::to::MyType)]` attribute to your field:
///
/// ```ignore
/// #[derive(Reflect)]
/// struct SomeStruct {
/// #[reflect(remote = RemoteTypeWrapper)]
/// data: RemoteType
/// }
/// ```
///
/// ## Safety
///
/// When using the `#[reflect(remote = path::to::MyType)]` field attribute, be sure you are defining the correct wrapper type.
/// Internally, this field will be unsafely [transmuted], and is only sound if using a wrapper generated for the remote type.
/// This also means keeping your wrapper definitions up-to-date with the remote types.
///
/// [transmuted]: std::mem::transmute
#[proc_macro_attribute]
pub fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
remote::reflect_remote(args, input)
}
/// A macro used to generate reflection trait implementations for the given type.
///
/// This is functionally the same as [deriving `Reflect`] using the `#[reflect(opaque)]` container attribute.
///
/// The only reason for this macro's existence is so that `bevy_reflect` can easily implement the reflection traits
/// on primitives and other opaque types internally.
///
/// Since this macro also implements `TypePath`, the type path must be explicit.
/// See [`impl_type_path!`] for the exact syntax.
///
/// # Examples
///
/// Types can be passed with or without registering type data:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!(my_crate::Foo);
/// impl_reflect_opaque!(my_crate::Bar(Debug, Default, Serialize, Deserialize));
/// ```
///
/// Generic types can also specify their parameters and bounds:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!(my_crate::Foo<T1, T2: Baz> where T1: Bar (Default, Serialize, Deserialize));
/// ```
///
/// Custom type paths can be specified:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_reflect_opaque!((in not_my_crate as NotFoo) Foo(Debug, Default));
/// ```
///
/// [deriving `Reflect`]: Reflect
#[proc_macro]
pub fn impl_reflect_opaque(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input with ReflectOpaqueDef::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() {
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|path| path.into_path(default_name)),
generics: &def.generics,
}
};
let meta = ReflectMeta::new(type_path, def.traits.unwrap_or_default());
#[cfg(feature = "documentation")]
let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs));
let reflect_impls = impls::impl_opaque(&meta);
let from_reflect_impl = from_reflect::impl_opaque(&meta);
TokenStream::from(quote! {
const _: () = {
#reflect_impls
#from_reflect_impl
};
})
}
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
/// the definitions of cannot be altered.
///
/// This macro is an alternative to [`impl_reflect_opaque!`] and [`impl_from_reflect_opaque!`]
/// which implement foreign types as Opaque 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`.
///
/// It is necessary to add a `#[type_path = "my_crate::foo"]` attribute to all types.
///
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
/// error message and fail to compile. If the type does not implement `Default`, it may not
/// be possible to reflect without extending the macro.
///
///
/// # Example
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// use bevy::prelude::Vec3;
///
/// impl_reflect!(
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// #[type_path = "bevy::prelude"]
/// struct Vec3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// );
/// ```
#[proc_macro]
pub fn impl_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
match_reflect_impls(ast, ReflectImplSource::ImplRemoteType)
}
/// A macro used to generate a `FromReflect` trait implementation for the given type.
///
/// This is functionally the same as [deriving `FromReflect`] on a type that [derives `Reflect`] using
/// the `#[reflect(opaque)]` container attribute.
///
/// The only reason this macro exists is so that `bevy_reflect` can easily implement `FromReflect` on
/// primitives and other opaque types internally.
///
/// Please note that this macro will not work with any type that [derives `Reflect`] normally
/// or makes use of the [`impl_reflect_opaque!`] macro, as those macros also implement `FromReflect`
/// by default.
///
/// # Examples
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_from_reflect_opaque!(foo<T1, T2: Baz> where T1: Bar);
/// ```
///
/// [deriving `FromReflect`]: FromReflect
/// [derives `Reflect`]: Reflect
#[proc_macro]
pub fn impl_from_reflect_opaque(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input with ReflectOpaqueDef::parse_from_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()
&& def.generics.params.is_empty()
{
ReflectTypePath::Primitive(default_name)
} else {
ReflectTypePath::External {
path: &def.type_path,
custom_path: def.custom_path.map(|alias| alias.into_path(default_name)),
generics: &def.generics,
}
};
let from_reflect_impl =
from_reflect::impl_opaque(&ReflectMeta::new(type_path, def.traits.unwrap_or_default()));
TokenStream::from(quote! {
const _: () = {
#from_reflect_impl
};
})
}
/// A replacement for [deriving `TypePath`] for use on foreign types.
///
/// Since (unlike the derive) this macro may be invoked in a different module to where the type is defined,
/// it requires an 'absolute' path definition.
///
/// Specifically, a leading `::` denoting a global path must be specified
/// or a preceding `(in my_crate::foo)` to specify the custom path must be used.
///
/// # Examples
///
/// Implementing `TypePath` on a foreign type:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(::foreign_crate::foo::bar::Baz);
/// ```
///
/// On a generic type (this can also accept trait bounds):
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(::foreign_crate::Foo<T>);
/// impl_type_path!(::foreign_crate::Goo<T: ?Sized>);
/// ```
///
/// On a primitive (note this will not compile for a non-primitive type):
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!(bool);
/// ```
///
/// With a custom type path:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!((in other_crate::foo::bar) Baz);
/// ```
///
/// With a custom type path and a custom type name:
/// ```ignore (bevy_reflect is not accessible from this crate)
/// impl_type_path!((in other_crate::foo as Baz) Bar);
/// ```
///
/// [deriving `TypePath`]: TypePath
#[proc_macro]
pub fn impl_type_path(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as NamedTypePathDef);
let type_path = match def {
NamedTypePathDef::External {
ref path,
custom_path,
ref generics,
} => {
let default_name = &path.segments.last().unwrap().ident;
ReflectTypePath::External {
path,
custom_path: custom_path.map(|path| path.into_path(default_name)),
generics,
}
}
NamedTypePathDef::Primitive(ref ident) => ReflectTypePath::Primitive(ident),
};
let meta = ReflectMeta::new(type_path, ContainerAttributes::default());
let type_path_impl = impls::impl_type_path(&meta);
TokenStream::from(quote! {
const _: () = {
#type_path_impl
};
})
}

View File

@@ -0,0 +1,7 @@
use bevy_macro_utils::BevyManifest;
use syn::Path;
/// Returns the correct path for `bevy_reflect`.
pub(crate) fn get_bevy_reflect_path() -> Path {
BevyManifest::shared().get_path("bevy_reflect")
}

View File

@@ -0,0 +1,75 @@
use crate::{
container_attributes::ContainerAttributes, derive_data::ReflectTraitToImpl,
type_path::CustomPathDef,
};
use syn::{parenthesized, parse::ParseStream, token::Paren, Attribute, Generics, Path};
/// A struct used to define a simple reflection-opaque types (including primitives).
///
/// This takes the form:
///
/// ```ignore (Method expecting TokenStream is better represented with raw tokens)
/// // Standard
/// ::my_crate::foo::Bar(TraitA, TraitB)
///
/// // With generics
/// ::my_crate::foo::Bar<T1: Bar, T2>(TraitA, TraitB)
///
/// // With generics and where clause
/// ::my_crate::foo::Bar<T1, T2> where T1: Bar (TraitA, TraitB)
///
/// // With a custom path (not with impl_from_reflect_opaque)
/// (in my_crate::bar) Bar(TraitA, TraitB)
/// ```
pub(crate) struct ReflectOpaqueDef {
#[cfg_attr(
not(feature = "documentation"),
expect(
dead_code,
reason = "The is used when the `documentation` feature is enabled.",
)
)]
pub attrs: Vec<Attribute>,
pub type_path: Path,
pub generics: Generics,
pub traits: Option<ContainerAttributes>,
pub custom_path: Option<CustomPathDef>,
}
impl ReflectOpaqueDef {
pub fn parse_reflect(input: ParseStream) -> syn::Result<Self> {
Self::parse(input, ReflectTraitToImpl::Reflect)
}
pub fn parse_from_reflect(input: ParseStream) -> syn::Result<Self> {
Self::parse(input, ReflectTraitToImpl::FromReflect)
}
fn parse(input: ParseStream, trait_: ReflectTraitToImpl) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let type_path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
let mut traits = None;
if input.peek(Paren) {
let content;
parenthesized!(content in input);
traits = Some({
let mut attrs = ContainerAttributes::default();
attrs.parse_terminated(&content, trait_)?;
attrs
});
}
Ok(Self {
attrs,
type_path,
generics,
traits,
custom_path,
})
}
}

View File

@@ -0,0 +1,63 @@
//! Contains code related specifically to Bevy's type registration.
use crate::{
derive_data::ReflectMeta, serialization::SerializationDataDef,
where_clause_options::WhereClauseOptions,
};
use quote::quote;
use syn::Type;
/// Creates the `GetTypeRegistration` impl for the given type data.
pub(crate) fn impl_get_type_registration<'a>(
meta: &ReflectMeta,
where_clause_options: &WhereClauseOptions,
serialization_data: Option<&SerializationDataDef>,
type_dependencies: Option<impl Iterator<Item = &'a Type>>,
) -> proc_macro2::TokenStream {
let type_path = meta.type_path();
let bevy_reflect_path = meta.bevy_reflect_path();
let registration_data = meta.attrs().idents();
let type_deps_fn = type_dependencies.map(|deps| {
quote! {
#[inline(never)]
fn register_type_dependencies(registry: &mut #bevy_reflect_path::TypeRegistry) {
#(<#deps as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry);)*
}
}
});
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
let from_reflect_data = if meta.from_reflect().should_auto_derive() {
Some(quote! {
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<Self>::from_type());
})
} else {
None
};
let serialization_data = serialization_data.map(|data| {
let serialization_data = data.as_serialization_data(bevy_reflect_path);
quote! {
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#serialization_data);
}
});
quote! {
#[allow(unused_mut)]
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<Self>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<Self>::from_type());
#from_reflect_data
#serialization_data
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<Self>::from_type());)*
registration
}
#type_deps_fn
}
}
}

502
vendor/bevy_reflect_derive/src/remote.rs vendored Normal file
View File

@@ -0,0 +1,502 @@
use crate::{
derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},
from_reflect,
ident::ident_or_index,
impls,
impls::impl_assertions,
ReflectDerive, REFLECT_ATTRIBUTE_NAME,
};
use bevy_macro_utils::fq_std::FQOption;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{format_ident, quote, quote_spanned};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
spanned::Spanned,
token::PathSep,
DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,
};
/// Generates the remote wrapper type and implements all the necessary traits.
pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
let remote_args = match syn::parse::<RemoteArgs>(args) {
Ok(path) => path,
Err(err) => return err.to_compile_error().into(),
};
let remote_ty = remote_args.remote_ty;
let ast = parse_macro_input!(input as DeriveInput);
let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);
let mut derive_data = match ReflectDerive::from_input(
&ast,
ReflectProvenance {
source: ReflectImplSource::RemoteReflect,
trait_: ReflectTraitToImpl::Reflect,
},
) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
derive_data.set_remote(Some(RemoteType::new(&remote_ty)));
let assertions = impl_assertions(&derive_data);
let definition_assertions = generate_remote_definition_assertions(&derive_data);
let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);
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::Opaque(meta) => (
impls::impl_opaque(&meta),
if meta.from_reflect().should_auto_derive() {
Some(from_reflect::impl_opaque(&meta))
} else {
None
},
),
};
TokenStream::from(quote! {
#wrapper_definition
const _: () = {
#reflect_remote_impl
#reflect_impls
#from_reflect_impl
#definition_assertions
#assertions
};
})
}
/// Generates the remote wrapper type.
///
/// # Example
///
/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:
///
/// ```
/// # struct Bar<T>(T);
///
/// #[repr(transparent)]
/// struct Foo<T>(Bar<T>);
/// ```
fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let ident = &input.ident;
let vis = &input.vis;
let ty_generics = &input.generics;
let where_clause = &input.generics.where_clause;
let attrs = input
.attrs
.iter()
.filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));
quote! {
#(#attrs)*
#[repr(transparent)]
#[doc(hidden)]
#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;
}
}
/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.
///
/// # Note to Developers
///
/// The `ReflectRemote` trait could likely be made with default method implementations.
/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.
/// To prevent this, we instead generate the implementation through a macro using this function.
fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {
let bevy_reflect_path = input.meta().bevy_reflect_path();
let type_path = input.meta().type_path();
let (impl_generics, ty_generics, where_clause) =
input.meta().type_path().generics().split_for_impl();
let where_reflect_clause = input
.where_clause_options()
.extend_where_clause(where_clause);
quote! {
// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.
impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {
type Remote = #remote_ty;
fn as_remote(&self) -> &Self::Remote {
&self.0
}
fn as_remote_mut(&mut self) -> &mut Self::Remote {
&mut self.0
}
fn into_remote(self) -> Self::Remote
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | core::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self, Self::Remote>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(self)
)
}
}
fn as_wrapper(remote: &Self::Remote) -> &Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }
}
fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }
}
fn into_wrapper(remote: Self::Remote) -> Self
{
// SAFE: The wrapper type should be repr(transparent) over the remote type
unsafe {
// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
// ```
// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
// |
// | core::mem::transmute::<A, B>(a)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// |
// = note: source type: `A` (this type does not have a fixed size)
// = note: target type: `B` (this type does not have a fixed size)
// ```
::core::mem::transmute_copy::<Self::Remote, Self>(
// `ManuallyDrop` is used to prevent double-dropping `self`
&::core::mem::ManuallyDrop::new(remote)
)
}
}
}
}
}
/// Generates compile-time assertions for remote fields.
///
/// This prevents types from using an invalid remote type.
/// this works by generating a struct, `RemoteFieldAssertions`, with methods that
/// will result in compile-time failure if types are mismatched.
/// The output of this function is best placed within an anonymous context to maintain hygiene.
///
/// # Example
///
/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.
///
/// ```ignore
/// mod external_crate {
/// pub struct TheirFoo(pub u32);
/// pub struct TheirBar(pub i32);
/// }
///
/// #[reflect_remote(external_crate::TheirFoo)]
/// struct MyFoo(pub u32);
/// #[reflect_remote(external_crate::TheirBar)]
/// struct MyBar(pub i32);
///
/// #[derive(Reflect)]
/// struct MyStruct {
/// #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
/// foo: external_crate::TheirFoo
/// }
/// ```
pub(crate) fn generate_remote_assertions(
derive_data: &ReflectDerive,
) -> Option<proc_macro2::TokenStream> {
struct RemoteAssertionData<'a> {
ident: Member,
variant: Option<&'a Ident>,
ty: &'a Type,
generics: &'a Generics,
remote_ty: &'a Type,
}
let bevy_reflect_path = derive_data.meta().bevy_reflect_path();
let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: None,
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})),
ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {
variant.active_fields().filter_map(|field| {
field
.attrs
.remote
.as_ref()
.map(|remote_ty| RemoteAssertionData {
ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),
variant: Some(&variant.data.ident),
ty: &field.data.ty,
generics: data.meta().type_path().generics(),
remote_ty,
})
})
})),
_ => return None,
};
let assertions = fields
.map(move |field| {
let ident = if let Some(variant) = field.variant {
format_ident!("{}__{}", variant, field.ident)
} else {
match field.ident {
Member::Named(ident) => ident,
Member::Unnamed(index) => format_ident!("field_{}", index),
}
};
let (impl_generics, _, where_clause) = field.generics.split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let ty = &field.ty;
let remote_ty = field.remote_ty;
let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);
let span = create_assertion_span(remote_ty.span());
quote_spanned! {span=>
#[allow(non_snake_case)]
#[allow(clippy::multiple_bound_locations)]
fn #assertion_ident #impl_generics () #where_reflect_clause {
let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {
None
})().unwrap();
}
}
})
.collect::<proc_macro2::TokenStream>();
if assertions.is_empty() {
None
} else {
Some(quote! {
struct RemoteFieldAssertions;
impl RemoteFieldAssertions {
#assertions
}
})
}
}
/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the
/// remote type it's wrapping.
///
/// Note: This currently results in "backwards" error messages like:
///
/// ```ignore
/// expected: <WRAPPER_FIELD_TYPE>
/// found: <REMOTE_FIELD_TYPE>
/// ```
///
/// Ideally it would be the other way around, but there's no easy way of doing this without
/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.
fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
let meta = derive_data.meta();
let self_ident = format_ident!("__remote__");
let self_ty = derive_data.remote_ty().unwrap().type_path();
let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();
let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();
let where_reflect_clause = derive_data
.where_clause_options()
.extend_where_clause(where_clause);
let assertions = match derive_data {
ReflectDerive::Struct(data)
| ReflectDerive::TupleStruct(data)
| ReflectDerive::UnitStruct(data) => {
let mut output = proc_macro2::TokenStream::new();
for field in data.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();
});
}
output
}
ReflectDerive::Enum(data) => {
let variants = data.variants().iter().map(|variant| {
let ident = &variant.data.ident;
let mut output = proc_macro2::TokenStream::new();
if variant.fields().is_empty() {
return quote!(#self_expr_path::#ident => {});
}
for field in variant.fields() {
let field_member =
ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let field_ident = format_ident!("field_{}", field_member);
let field_ty = &field.data.ty;
let span = create_assertion_span(field_ty.span());
output.extend(quote_spanned! {span=>
#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {
#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();
}
});
}
output
});
quote! {
match #self_ident {
#(#variants)*
}
}
}
ReflectDerive::Opaque(_) => {
// No assertions needed since there are no fields to check
proc_macro2::TokenStream::new()
}
};
quote! {
const _: () = {
#[allow(non_snake_case)]
#[allow(unused_variables)]
#[allow(unused_assignments)]
#[allow(unreachable_patterns)]
#[allow(clippy::multiple_bound_locations)]
fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {
#assertions
}
};
}
}
/// Creates a span located around the given one, but resolves to the assertion's context.
///
/// This should allow the compiler to point back to the line and column in the user's code,
/// while still attributing the error to the macro.
fn create_assertion_span(span: Span) -> Span {
Span::call_site().located_at(span)
}
/// A reflected type's remote type.
///
/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
#[derive(Copy, Clone)]
pub(crate) struct RemoteType<'a> {
path: &'a TypePath,
}
impl<'a> RemoteType<'a> {
pub fn new(path: &'a TypePath) -> Self {
Self { path }
}
/// Returns the [type path](TypePath) of this remote type.
pub fn type_path(&self) -> &'a TypePath {
self.path
}
/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).
///
/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.
///
/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.
pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {
let mut expr_path = self.path.clone();
if let Some(segment) = expr_path.path.segments.last_mut() {
match &mut segment.arguments {
PathArguments::None => {}
PathArguments::AngleBracketed(arg) => {
arg.colon2_token = Some(PathSep::default());
}
PathArguments::Parenthesized(arg) => {
return Err(syn::Error::new(
arg.span(),
"cannot use parenthesized type as remote type",
))
}
}
}
Ok(ExprPath {
path: expr_path.path,
qself: expr_path.qself,
attrs: Vec::new(),
})
}
}
/// Metadata from the arguments defined in the `reflect_remote` attribute.
///
/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.
struct RemoteArgs {
remote_ty: TypePath,
}
impl Parse for RemoteArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
remote_ty: input.parse()?,
})
}
}

View File

@@ -0,0 +1,46 @@
/// Helper struct used to process an iterator of `Result<Vec<T>, syn::Error>`,
/// combining errors into one along the way.
pub(crate) struct ResultSifter<T> {
items: Vec<T>,
errors: Option<syn::Error>,
}
impl<T> Default for ResultSifter<T> {
fn default() -> Self {
Self {
items: Vec::new(),
errors: None,
}
}
}
impl<T> ResultSifter<T> {
/// Sift the given result, combining errors if necessary.
pub fn sift(&mut self, result: Result<T, syn::Error>) {
match result {
Ok(data) => self.items.push(data),
Err(err) => {
if let Some(ref mut errors) = self.errors {
errors.combine(err);
} else {
self.errors = Some(err);
}
}
}
}
/// Associated method that provides a convenient implementation for [`Iterator::fold`].
pub fn fold(mut sifter: Self, result: Result<T, syn::Error>) -> Self {
sifter.sift(result);
sifter
}
/// Complete the sifting process and return the final result.
pub fn finish(self) -> Result<Vec<T>, syn::Error> {
if let Some(errors) = self.errors {
Err(errors)
} else {
Ok(self.items)
}
}
}

View File

@@ -0,0 +1,95 @@
use crate::{
derive_data::StructField,
field_attributes::{DefaultBehavior, ReflectIgnoreBehavior},
};
use bevy_macro_utils::fq_std::FQDefault;
use quote::quote;
use std::collections::HashMap;
use syn::{spanned::Spanned, Path};
type ReflectionIndex = usize;
/// Collected serialization data used to generate a `SerializationData` type.
pub(crate) struct SerializationDataDef {
/// Maps a field's _reflection_ index to its [`SkippedFieldDef`] if marked as `#[reflect(skip_serializing)]`.
skipped: HashMap<ReflectionIndex, SkippedFieldDef>,
}
impl SerializationDataDef {
/// Attempts to create a new `SerializationDataDef` from the given collection of fields.
///
/// Returns `Ok(Some(data))` if there are any fields needing to be skipped during serialization.
/// Otherwise, returns `Ok(None)`.
pub fn new(
fields: &[StructField<'_>],
bevy_reflect_path: &Path,
) -> Result<Option<Self>, syn::Error> {
let mut skipped = <HashMap<_, _>>::default();
for field in fields {
match field.attrs.ignore {
ReflectIgnoreBehavior::IgnoreSerialization => {
skipped.insert(
field.reflection_index.ok_or_else(|| {
syn::Error::new(
field.data.span(),
"internal error: field is missing a reflection index",
)
})?,
SkippedFieldDef::new(field, bevy_reflect_path)?,
);
}
_ => continue,
}
}
if skipped.is_empty() {
Ok(None)
} else {
Ok(Some(Self { skipped }))
}
}
/// Returns a `TokenStream` containing an initialized `SerializationData` type.
pub fn as_serialization_data(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
let fields =
self.skipped
.iter()
.map(|(reflection_index, SkippedFieldDef { default_fn })| {
quote! {(
#reflection_index,
#bevy_reflect_path::serde::SkippedField::new(#default_fn)
)}
});
quote! {
#bevy_reflect_path::serde::SerializationData::new(
::core::iter::IntoIterator::into_iter([#(#fields),*])
)
}
}
}
/// Collected field data used to generate a `SkippedField` type.
pub(crate) struct SkippedFieldDef {
/// The default function for this field.
///
/// This is of type `fn() -> Box<dyn Reflect>`.
default_fn: proc_macro2::TokenStream,
}
impl SkippedFieldDef {
pub fn new(field: &StructField<'_>, bevy_reflect_path: &Path) -> Result<Self, syn::Error> {
let ty = &field.data.ty;
let default_fn = match &field.attrs.default {
DefaultBehavior::Func(func) => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#func()) }
},
_ => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<#ty as #FQDefault>::default()) }
},
};
Ok(Self { default_fn })
}
}

View File

@@ -0,0 +1,108 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, LitStr};
/// Contains tokens representing different kinds of string.
#[derive(Clone)]
pub(crate) enum StringExpr {
/// A string that is valid at compile time.
///
/// This is either a string literal like `"mystring"`,
/// or a string created by a macro like [`module_path`]
/// or [`concat`].
Const(TokenStream),
/// A [string slice](str) that is borrowed for a `'static` lifetime.
Borrowed(TokenStream),
/// An [owned string](String).
Owned(TokenStream),
}
impl<T: ToString + Spanned> From<T> for StringExpr {
fn from(value: T) -> Self {
Self::from_lit(&LitStr::new(&value.to_string(), value.span()))
}
}
impl StringExpr {
/// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_lit(lit: &LitStr) -> Self {
Self::Const(lit.to_token_stream())
}
/// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`].
///
/// [constant]: StringExpr::Const
pub fn from_str(string: &str) -> Self {
Self::Const(string.into_token_stream())
}
/// Returns tokens for an [owned string](String).
///
/// The returned expression will allocate unless the [`StringExpr`] is [already owned].
///
/// [already owned]: StringExpr::Owned
pub fn into_owned(self) -> TokenStream {
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => quote! {
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(#tokens)
},
Self::Owned(owned) => owned,
}
}
/// Returns tokens for a statically borrowed [string slice](str).
pub fn into_borrowed(self) -> TokenStream {
match self {
Self::Const(tokens) | Self::Borrowed(tokens) => tokens,
Self::Owned(owned) => quote! {
&#owned
},
}
}
/// Appends a [`StringExpr`] to another.
///
/// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them.
pub fn appended_by(mut self, other: StringExpr) -> Self {
if let Self::Const(tokens) = self {
if let Self::Const(more) = other {
return Self::Const(quote! {
::core::concat!(#tokens, #more)
});
}
self = Self::Const(tokens);
}
let owned = self.into_owned();
let borrowed = other.into_borrowed();
Self::Owned(quote! {
::core::ops::Add::<&str>::add(#owned, #borrowed)
})
}
}
impl Default for StringExpr {
fn default() -> Self {
StringExpr::from_str("")
}
}
impl FromIterator<StringExpr> for StringExpr {
fn from_iter<T: IntoIterator<Item = StringExpr>>(iter: T) -> Self {
let mut iter = iter.into_iter();
match iter.next() {
Some(mut expr) => {
for next in iter {
expr = expr.appended_by(next);
}
expr
}
None => Default::default(),
}
}
}

View File

@@ -0,0 +1,42 @@
use crate::ReflectStruct;
/// A helper struct for creating remote-aware field accessors.
///
/// These are "remote-aware" because when a field is a remote field, it uses a [`transmute`] internally
/// to access the field.
///
/// [`transmute`]: std::mem::transmute
pub(crate) struct FieldAccessors {
/// The referenced field accessors, such as `&self.foo`.
pub fields_ref: Vec<proc_macro2::TokenStream>,
/// The mutably referenced field accessors, such as `&mut self.foo`.
pub fields_mut: Vec<proc_macro2::TokenStream>,
/// The ordered set of field indices (basically just the range of [0, `field_count`).
pub field_indices: Vec<usize>,
/// The number of fields in the reflected struct.
pub field_count: usize,
}
impl FieldAccessors {
pub fn new(reflect_struct: &ReflectStruct) -> Self {
let (fields_ref, fields_mut): (Vec<_>, Vec<_>) = reflect_struct
.active_fields()
.map(|field| {
(
reflect_struct.access_for_field(field, false),
reflect_struct.access_for_field(field, true),
)
})
.unzip();
let field_count = fields_ref.len();
let field_indices = (0..field_count).collect();
Self {
fields_ref,
fields_mut,
field_indices,
field_count,
}
}
}

View File

@@ -0,0 +1,93 @@
use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token};
pub(crate) struct TraitInfo {
item_trait: ItemTrait,
}
impl Parse for TraitInfo {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let lookahead = input.lookahead1();
if lookahead.peek(Token![pub]) || lookahead.peek(Token![trait]) {
let mut item_trait: ItemTrait = input.parse()?;
item_trait.attrs = attrs;
Ok(TraitInfo { item_trait })
} else {
Err(lookahead.error())
}
}
}
/// A trait attribute macro that allows a reflected type to be downcast to a trait object.
///
/// This generates a struct that takes the form `ReflectMyTrait`. An instance of this struct can then be
/// used to perform the conversion.
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;
let trait_vis = &item_trait.vis;
let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident.to_string());
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
let struct_doc = format!(
" A type generated by the #[reflect_trait] macro for the `{trait_ident}` trait.\n\n This allows casting from `dyn Reflect` to `dyn {trait_ident}`.",
);
let get_doc = format!(
" Downcast a `&dyn Reflect` type to `&dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
);
let get_mut_doc = format!(
" Downcast a `&mut dyn Reflect` type to `&mut dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
);
let get_box_doc = format!(
" Downcast a `Box<dyn Reflect>` type to `Box<dyn {trait_ident}>`.\n\n If the type cannot be downcast, this will return `Err(Box<dyn Reflect>)`.",
);
TokenStream::from(quote! {
#item_trait
#[doc = #struct_doc]
#[derive(#FQClone)]
#trait_vis struct #reflect_trait_ident {
get_func: fn(&dyn #bevy_reflect_path::Reflect) -> #FQOption<&dyn #trait_ident>,
get_mut_func: fn(&mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&mut dyn #trait_ident>,
get_boxed_func: fn(#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>>,
}
impl #reflect_trait_ident {
#[doc = #get_doc]
pub fn get<'a>(&self, reflect_value: &'a dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a dyn #trait_ident> {
(self.get_func)(reflect_value)
}
#[doc = #get_mut_doc]
pub fn get_mut<'a>(&self, reflect_value: &'a mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a mut dyn #trait_ident> {
(self.get_mut_func)(reflect_value)
}
#[doc = #get_box_doc]
pub fn get_boxed(&self, reflect_value: #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>> {
(self.get_boxed_func)(reflect_value)
}
}
impl<T: #trait_ident + #bevy_reflect_path::Reflect> #bevy_reflect_path::FromType<T> for #reflect_trait_ident {
fn from_type() -> Self {
Self {
get_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast_ref::<T>(reflect_value).map(|value| value as &dyn #trait_ident)
},
get_mut_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast_mut::<T>(reflect_value).map(|value| value as &mut dyn #trait_ident)
},
get_boxed_func: |reflect_value| {
<dyn #bevy_reflect_path::Reflect>::downcast::<T>(reflect_value).map(|value| value as #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>)
}
}
}
}
})
}

View File

@@ -0,0 +1,102 @@
use proc_macro2::Ident;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
token::Paren,
Generics, Path, PathSegment, Token,
};
pub(crate) fn parse_path_no_leading_colon(input: ParseStream) -> syn::Result<Path> {
if input.peek(Token![::]) {
return Err(input.error("did not expect a leading double colon (`::`)"));
}
let path = Path::parse_mod_style(input)?;
if path.segments.is_empty() {
Err(input.error("expected a path"))
} else {
Ok(path)
}
}
/// An alias for a `TypePath`.
///
/// This is the parenthesized part of `(in my_crate::foo as MyType) SomeType`.
pub(crate) struct CustomPathDef {
path: Path,
name: Option<Ident>,
}
impl CustomPathDef {
pub fn into_path(mut self, default_name: &Ident) -> Path {
let name = PathSegment::from(self.name.unwrap_or_else(|| default_name.clone()));
self.path.segments.push(name);
self.path
}
pub fn parse_parenthesized(input: ParseStream) -> syn::Result<Option<Self>> {
if input.peek(Paren) {
let path;
parenthesized!(path in input);
Ok(Some(path.call(Self::parse)?))
} else {
Ok(None)
}
}
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse::<Token![in]>()?;
let custom_path = parse_path_no_leading_colon(input)?;
if !input.peek(Token![as]) {
return Ok(Self {
path: custom_path,
name: None,
});
}
input.parse::<Token![as]>()?;
let custom_name: Ident = input.parse()?;
Ok(Self {
path: custom_path,
name: Some(custom_name),
})
}
}
pub(crate) enum NamedTypePathDef {
External {
path: Path,
generics: Generics,
custom_path: Option<CustomPathDef>,
},
Primitive(Ident),
}
impl Parse for NamedTypePathDef {
fn parse(input: ParseStream) -> syn::Result<Self> {
let custom_path = CustomPathDef::parse_parenthesized(input)?;
let path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
if path.leading_colon.is_none() && custom_path.is_none() {
if path.segments.len() == 1 {
let ident = path.segments.into_iter().next().unwrap().ident;
Ok(NamedTypePathDef::Primitive(ident))
} else {
Err(input.error("non-customized paths must start with a double colon (`::`)"))
}
} else {
Ok(NamedTypePathDef::External {
path,
generics,
custom_path,
})
}
}
}

View File

@@ -0,0 +1,198 @@
use crate::derive_data::ReflectMeta;
use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{punctuated::Punctuated, Token, Type, WhereClause};
/// Options defining how to extend the `where` clause for reflection.
pub(crate) struct WhereClauseOptions<'a, 'b> {
meta: &'a ReflectMeta<'b>,
active_fields: Box<[Type]>,
}
impl<'a, 'b> WhereClauseOptions<'a, 'b> {
pub fn new(meta: &'a ReflectMeta<'b>) -> Self {
Self {
meta,
active_fields: Box::new([]),
}
}
pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self {
Self {
meta,
active_fields,
}
}
/// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
///
/// The default bounds added are as follows:
/// - `Self` has the bounds `Any + Send + Sync`
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
/// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
/// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
///
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// #[derive(Reflect)]
/// struct Foo<T, U> {
/// a: T,
/// #[reflect(ignore)]
/// b: U
/// }
/// ```
///
/// Generates the following where clause:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Field bounds
/// T: FromReflect + TypePath,
/// ```
///
/// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Field bounds
/// T: FromReflect + TypePath,
/// // Custom bounds
/// T: MyTrait,
/// ```
///
/// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
///
/// ```ignore (bevy_reflect is not accessible from this crate)
/// where
/// // `Self` bounds:
/// Self: Any + Send + Sync,
/// // Type parameter bounds:
/// T: TypePath,
/// U: TypePath,
/// // Custom bounds
/// T: MyTrait,
/// ```
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
// We would normally just use `Self`, but that won't work for generating things like assertion functions
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
let this = self.meta.type_path().true_type();
let required_bounds = self.required_bounds();
// Maintain existing where clause, if any.
let mut generic_where_clause = if let Some(where_clause) = where_clause {
let predicates = where_clause.predicates.iter();
quote! {where #this: #required_bounds, #(#predicates,)*}
} else {
quote!(where #this: #required_bounds,)
};
// Add additional reflection trait bounds
let predicates = self.predicates();
generic_where_clause.extend(quote! {
#predicates
});
generic_where_clause
}
/// Returns an iterator the where clause predicates to extended the where clause with.
fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {
let mut predicates = Punctuated::new();
if let Some(type_param_predicates) = self.type_param_predicates() {
predicates.extend(type_param_predicates);
}
if let Some(field_predicates) = self.active_field_predicates() {
predicates.extend(field_predicates);
}
if let Some(custom_where) = self.meta.attrs().custom_where() {
predicates.push(custom_where.predicates.to_token_stream());
}
predicates
}
/// Returns an iterator over the where clause predicates for the type parameters
/// if they require one.
fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
self.type_path_bound().map(|type_path_bound| {
self.meta
.type_path()
.generics()
.type_params()
.map(move |param| {
let ident = &param.ident;
quote!(#ident : #type_path_bound)
})
})
}
/// Returns an iterator over the where clause predicates for the active fields.
fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
if self.meta.attrs().no_field_bounds() {
None
} else {
let bevy_reflect_path = self.meta.bevy_reflect_path();
let reflect_bound = self.reflect_bound();
// `TypePath` is always required for active fields since they are used to
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
// Likewise, `GetTypeRegistration` is always required for active fields since
// they are used to register the type's dependencies.
Some(self.active_fields.iter().map(move |ty| {
quote!(
#ty : #reflect_bound
+ #bevy_reflect_path::TypePath
// Needed for `Typed` impls
+ #bevy_reflect_path::MaybeTyped
// Needed for `GetTypeRegistration` impls
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
)
}))
}
}
/// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.
fn reflect_bound(&self) -> TokenStream {
let bevy_reflect_path = self.meta.bevy_reflect_path();
if self.meta.from_reflect().should_auto_derive() {
quote!(#bevy_reflect_path::FromReflect)
} else {
quote!(#bevy_reflect_path::PartialReflect)
}
}
/// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.
fn type_path_bound(&self) -> Option<TokenStream> {
if self.meta.type_path_attrs().should_auto_derive() {
let bevy_reflect_path = self.meta.bevy_reflect_path();
Some(quote!(#bevy_reflect_path::TypePath))
} else {
None
}
}
/// The minimum required bounds for a type to be reflected.
fn required_bounds(&self) -> TokenStream {
quote!(#FQAny + #FQSend + #FQSync)
}
}