Path: blob/main/crates/bevy_reflect/derive/src/derive_data.rs
6599 views
use core::fmt;1use indexmap::IndexSet;2use proc_macro2::Span;34use crate::{5container_attributes::{ContainerAttributes, FromReflectAttrs, TypePathAttrs},6field_attributes::FieldAttributes,7remote::RemoteType,8result_sifter::ResultSifter,9serialization::SerializationDataDef,10string_expr::StringExpr,11type_path::parse_path_no_leading_colon,12where_clause_options::WhereClauseOptions,13REFLECT_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME, TYPE_PATH_ATTRIBUTE_NAME,14};15use quote::{format_ident, quote, ToTokens};16use syn::token::Comma;1718use crate::enum_utility::{EnumVariantOutputData, ReflectCloneVariantBuilder, VariantBuilder};19use crate::field_attributes::CloneBehavior;20use crate::generics::generate_generics;21use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};22use syn::{23parse_str, punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Field, Fields,24GenericParam, Generics, Ident, LitStr, Member, Meta, Path, PathSegment, Type, TypeParam,25Variant,26};2728pub(crate) enum ReflectDerive<'a> {29Struct(ReflectStruct<'a>),30TupleStruct(ReflectStruct<'a>),31UnitStruct(ReflectStruct<'a>),32Enum(ReflectEnum<'a>),33Opaque(ReflectMeta<'a>),34}3536/// Metadata present on all reflected types, including name, generics, and attributes.37///38/// # Example39///40/// ```ignore (bevy_reflect is not accessible from this crate)41/// #[derive(Reflect)]42/// // traits43/// // |----------------------------------------|44/// #[reflect(PartialEq, Serialize, Deserialize, Default)]45/// // type_path generics46/// // |-------------------||----------|47/// struct ThingThatImReflecting<T1, T2, T3> {/* ... */}48/// ```49pub(crate) struct ReflectMeta<'a> {50/// The registered traits for this type.51attrs: ContainerAttributes,52/// The path to this type.53type_path: ReflectTypePath<'a>,54/// The optional remote type to use instead of the actual type.55remote_ty: Option<RemoteType<'a>>,56/// A cached instance of the path to the `bevy_reflect` crate.57bevy_reflect_path: Path,58/// The documentation for this type, if any59#[cfg(feature = "documentation")]60docs: crate::documentation::Documentation,61}6263/// Struct data used by derive macros for `Reflect` and `FromReflect`.64///65/// # Example66///67/// ```ignore (bevy_reflect is not accessible from this crate)68/// #[derive(Reflect)]69/// #[reflect(PartialEq, Serialize, Deserialize, Default)]70/// struct ThingThatImReflecting<T1, T2, T3> {71/// x: T1, // |72/// y: T2, // |- fields73/// z: T3 // |74/// }75/// ```76pub(crate) struct ReflectStruct<'a> {77meta: ReflectMeta<'a>,78serialization_data: Option<SerializationDataDef>,79fields: Vec<StructField<'a>>,80}8182/// Enum data used by derive macros for `Reflect` and `FromReflect`.83///84/// # Example85///86/// ```ignore (bevy_reflect is not accessible from this crate)87/// #[derive(Reflect)]88/// #[reflect(PartialEq, Serialize, Deserialize, Default)]89/// enum ThingThatImReflecting<T1, T2, T3> {90/// A(T1), // |91/// B, // |- variants92/// C { foo: T2, bar: T3 } // |93/// }94/// ```95pub(crate) struct ReflectEnum<'a> {96meta: ReflectMeta<'a>,97variants: Vec<EnumVariant<'a>>,98}99100/// Represents a field on a struct or tuple struct.101#[derive(Clone)]102pub(crate) struct StructField<'a> {103/// The raw field.104pub data: &'a Field,105/// The reflection-based attributes on the field.106pub attrs: FieldAttributes,107/// The index of this field within the struct.108pub declaration_index: usize,109/// The index of this field as seen by the reflection API.110///111/// This index accounts for the removal of [ignored] fields.112/// It will only be `Some(index)` when the field is not ignored.113///114/// [ignored]: crate::field_attributes::ReflectIgnoreBehavior::IgnoreAlways115pub reflection_index: Option<usize>,116/// The documentation for this field, if any117#[cfg(feature = "documentation")]118pub doc: crate::documentation::Documentation,119}120121/// Represents a variant on an enum.122pub(crate) struct EnumVariant<'a> {123/// The raw variant.124pub data: &'a Variant,125/// The fields within this variant.126pub fields: EnumVariantFields<'a>,127/// The reflection-based attributes on the variant.128pub attrs: FieldAttributes,129/// The documentation for this variant, if any130#[cfg(feature = "documentation")]131pub doc: crate::documentation::Documentation,132}133134pub(crate) enum EnumVariantFields<'a> {135Named(Vec<StructField<'a>>),136Unnamed(Vec<StructField<'a>>),137Unit,138}139140/// How the macro was invoked.141#[derive(Debug, Copy, Clone, PartialEq, Eq)]142pub(crate) enum ReflectImplSource {143/// Using `impl_reflect!`.144ImplRemoteType,145/// Using `#[derive(...)]`.146DeriveLocalType,147/// Using `#[reflect_remote]`.148RemoteReflect,149}150151/// Which trait the macro explicitly implements.152#[derive(Debug, Copy, Clone, PartialEq, Eq)]153pub(crate) enum ReflectTraitToImpl {154Reflect,155FromReflect,156TypePath,157}158159/// The provenance of a macro invocation.160#[derive(Debug, Copy, Clone, PartialEq, Eq)]161pub(crate) struct ReflectProvenance {162pub source: ReflectImplSource,163pub trait_: ReflectTraitToImpl,164}165166impl fmt::Display for ReflectProvenance {167fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {168use self::{ReflectImplSource as S, ReflectTraitToImpl as T};169let str = match (self.source, self.trait_) {170(S::ImplRemoteType, T::Reflect) => "`impl_reflect`",171(S::DeriveLocalType, T::Reflect) => "`#[derive(Reflect)]`",172(S::DeriveLocalType, T::FromReflect) => "`#[derive(FromReflect)]`",173(S::DeriveLocalType, T::TypePath) => "`#[derive(TypePath)]`",174(S::RemoteReflect, T::Reflect) => "`#[reflect_remote]`",175(S::RemoteReflect, T::FromReflect | T::TypePath)176| (S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),177};178f.write_str(str)179}180}181182impl<'a> ReflectDerive<'a> {183pub fn from_input(184input: &'a DeriveInput,185provenance: ReflectProvenance,186) -> Result<Self, syn::Error> {187let mut container_attributes = ContainerAttributes::default();188// Should indicate whether `#[type_path = "..."]` was used.189let mut custom_path: Option<Path> = None;190// Should indicate whether `#[type_name = "..."]` was used.191let mut custom_type_name: Option<Ident> = None;192193#[cfg(feature = "documentation")]194let mut doc = crate::documentation::Documentation::default();195196for attribute in &input.attrs {197match &attribute.meta {198Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {199container_attributes.parse_meta_list(meta_list, provenance.trait_)?;200}201Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {202let syn::Expr::Lit(syn::ExprLit {203lit: syn::Lit::Str(lit),204..205}) = &pair.value206else {207return Err(syn::Error::new(208pair.span(),209format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),210));211};212213custom_path = Some(syn::parse::Parser::parse_str(214parse_path_no_leading_colon,215&lit.value(),216)?);217}218Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {219let syn::Expr::Lit(syn::ExprLit {220lit: syn::Lit::Str(lit),221..222}) = &pair.value223else {224return Err(syn::Error::new(225pair.span(),226format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),227));228};229230custom_type_name = Some(parse_str(&lit.value())?);231}232#[cfg(feature = "documentation")]233Meta::NameValue(pair) if pair.path.is_ident("doc") => {234if let syn::Expr::Lit(syn::ExprLit {235lit: syn::Lit::Str(lit),236..237}) = &pair.value238{239doc.push(lit.value());240}241}242_ => continue,243}244}245match (&mut custom_path, custom_type_name) {246(Some(path), custom_type_name) => {247let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());248path.segments.push(PathSegment::from(ident));249}250(None, Some(name)) => {251return Err(syn::Error::new(252name.span(),253format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),254));255}256_ => (),257}258259let type_path = ReflectTypePath::Internal {260ident: &input.ident,261custom_path,262generics: &input.generics,263};264265let meta = ReflectMeta::new(type_path, container_attributes);266267if provenance.source == ReflectImplSource::ImplRemoteType268&& meta.type_path_attrs().should_auto_derive()269&& !meta.type_path().has_custom_path()270{271return Err(syn::Error::new(272meta.type_path().span(),273format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}"),274));275}276277#[cfg(feature = "documentation")]278let meta = meta.with_docs(doc);279280if meta.attrs().is_opaque() {281return Ok(Self::Opaque(meta));282}283284match &input.data {285Data::Struct(data) => {286let fields = Self::collect_struct_fields(&data.fields)?;287let serialization_data =288SerializationDataDef::new(&fields, &meta.bevy_reflect_path)?;289let reflect_struct = ReflectStruct {290meta,291serialization_data,292fields,293};294295match data.fields {296Fields::Named(..) => Ok(Self::Struct(reflect_struct)),297Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)),298Fields::Unit => Ok(Self::UnitStruct(reflect_struct)),299}300}301Data::Enum(data) => {302let variants = Self::collect_enum_variants(&data.variants)?;303304let reflect_enum = ReflectEnum { meta, variants };305Ok(Self::Enum(reflect_enum))306}307Data::Union(..) => Err(syn::Error::new(308input.span(),309"reflection not supported for unions",310)),311}312}313314/// Set the remote type for this derived type.315///316/// # Panics317///318/// Panics when called on [`ReflectDerive::Opaque`].319pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {320match self {321Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {322data.meta.remote_ty = remote_ty;323}324Self::Enum(data) => {325data.meta.remote_ty = remote_ty;326}327Self::Opaque(meta) => {328meta.remote_ty = remote_ty;329}330}331}332333/// Get the remote type path, if any.334pub fn remote_ty(&self) -> Option<RemoteType<'_>> {335match self {336Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {337data.meta.remote_ty()338}339Self::Enum(data) => data.meta.remote_ty(),340Self::Opaque(meta) => meta.remote_ty(),341}342}343344/// Get the [`ReflectMeta`] for this derived type.345pub fn meta(&self) -> &ReflectMeta<'_> {346match self {347Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),348Self::Enum(data) => data.meta(),349Self::Opaque(meta) => meta,350}351}352353pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {354match self {355Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {356data.where_clause_options()357}358Self::Enum(data) => data.where_clause_options(),359Self::Opaque(meta) => WhereClauseOptions::new(meta),360}361}362363fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {364let mut active_index = 0;365let sifter: ResultSifter<StructField<'a>> = fields366.iter()367.enumerate()368.map(369|(declaration_index, field)| -> Result<StructField, syn::Error> {370let attrs = FieldAttributes::parse_attributes(&field.attrs)?;371372let reflection_index = if attrs.ignore.is_ignored() {373None374} else {375active_index += 1;376Some(active_index - 1)377};378379Ok(StructField {380declaration_index,381reflection_index,382attrs,383data: field,384#[cfg(feature = "documentation")]385doc: crate::documentation::Documentation::from_attributes(&field.attrs),386})387},388)389.fold(ResultSifter::default(), ResultSifter::fold);390391sifter.finish()392}393394fn collect_enum_variants(395variants: &'a Punctuated<Variant, Comma>,396) -> Result<Vec<EnumVariant<'a>>, syn::Error> {397let sifter: ResultSifter<EnumVariant<'a>> = variants398.iter()399.map(|variant| -> Result<EnumVariant, syn::Error> {400let fields = Self::collect_struct_fields(&variant.fields)?;401402let fields = match variant.fields {403Fields::Named(..) => EnumVariantFields::Named(fields),404Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),405Fields::Unit => EnumVariantFields::Unit,406};407Ok(EnumVariant {408fields,409attrs: FieldAttributes::parse_attributes(&variant.attrs)?,410data: variant,411#[cfg(feature = "documentation")]412doc: crate::documentation::Documentation::from_attributes(&variant.attrs),413})414})415.fold(ResultSifter::default(), ResultSifter::fold);416417sifter.finish()418}419}420421impl<'a> ReflectMeta<'a> {422pub fn new(type_path: ReflectTypePath<'a>, attrs: ContainerAttributes) -> Self {423Self {424attrs,425type_path,426remote_ty: None,427bevy_reflect_path: crate::meta::get_bevy_reflect_path(),428#[cfg(feature = "documentation")]429docs: Default::default(),430}431}432433/// Sets the documentation for this type.434#[cfg(feature = "documentation")]435pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {436Self { docs, ..self }437}438439/// The registered reflect attributes on this struct.440pub fn attrs(&self) -> &ContainerAttributes {441&self.attrs442}443444/// The `FromReflect` attributes on this type.445#[expect(446clippy::wrong_self_convention,447reason = "Method returns `FromReflectAttrs`, does not actually convert data."448)]449pub fn from_reflect(&self) -> &FromReflectAttrs {450self.attrs.from_reflect_attrs()451}452453/// The `TypePath` attributes on this type.454pub fn type_path_attrs(&self) -> &TypePathAttrs {455self.attrs.type_path_attrs()456}457458/// The path to this type.459pub fn type_path(&self) -> &ReflectTypePath<'a> {460&self.type_path461}462463/// Get the remote type path, if any.464pub fn remote_ty(&self) -> Option<RemoteType<'_>> {465self.remote_ty466}467468/// Whether this reflected type represents a remote type or not.469pub fn is_remote_wrapper(&self) -> bool {470self.remote_ty.is_some()471}472473/// The cached `bevy_reflect` path.474pub fn bevy_reflect_path(&self) -> &Path {475&self.bevy_reflect_path476}477478/// Returns the `GetTypeRegistration` impl as a `TokenStream`.479pub fn get_type_registration(480&self,481where_clause_options: &WhereClauseOptions,482) -> proc_macro2::TokenStream {483crate::registration::impl_get_type_registration(484where_clause_options,485None,486Option::<core::iter::Empty<&Type>>::None,487)488}489490/// The collection of docstrings for this type, if any.491#[cfg(feature = "documentation")]492pub fn doc(&self) -> &crate::documentation::Documentation {493&self.docs494}495}496497impl<'a> StructField<'a> {498/// Generates a `TokenStream` for `NamedField` or `UnnamedField` construction.499pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {500let name = match &self.data.ident {501Some(ident) => ident.to_string().to_token_stream(),502None => self.reflection_index.to_token_stream(),503};504505let field_info = if self.data.ident.is_some() {506quote! {507#bevy_reflect_path::NamedField508}509} else {510quote! {511#bevy_reflect_path::UnnamedField512}513};514515let ty = self.reflected_type();516517let mut info = quote! {518#field_info::new::<#ty>(#name)519};520521let custom_attributes = &self.attrs.custom_attributes;522if !custom_attributes.is_empty() {523let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);524info.extend(quote! {525.with_custom_attributes(#custom_attributes)526});527}528529#[cfg(feature = "documentation")]530{531let docs = &self.doc;532if !docs.is_empty() {533info.extend(quote! {534.with_docs(#docs)535});536}537}538539info540}541542/// Returns the reflected type of this field.543///544/// Normally this is just the field's defined type.545/// However, this can be adjusted to use a different type, like for representing remote types.546/// In those cases, the returned value is the remote wrapper type.547pub fn reflected_type(&self) -> &Type {548self.attrs.remote.as_ref().unwrap_or(&self.data.ty)549}550551pub fn attrs(&self) -> &FieldAttributes {552&self.attrs553}554555/// Generates a [`Member`] based on this field.556///557/// If the field is unnamed, the declaration index is used.558/// This allows this member to be used for both active and ignored fields.559pub fn to_member(&self) -> Member {560match &self.data.ident {561Some(ident) => Member::Named(ident.clone()),562None => Member::Unnamed(self.declaration_index.into()),563}564}565566/// Returns a token stream for generating a `FieldId` for this field.567pub fn field_id(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {568match &self.data.ident {569Some(ident) => {570let name = ident.to_string();571quote!(#bevy_reflect_path::FieldId::Named(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name)))572}573None => {574let index = self.declaration_index;575quote!(#bevy_reflect_path::FieldId::Unnamed(#index))576}577}578}579}580581impl<'a> ReflectStruct<'a> {582/// Access the metadata associated with this struct definition.583pub fn meta(&self) -> &ReflectMeta<'a> {584&self.meta585}586587/// Returns the [`SerializationDataDef`] for this struct.588pub fn serialization_data(&self) -> Option<&SerializationDataDef> {589self.serialization_data.as_ref()590}591592/// Returns the `GetTypeRegistration` impl as a `TokenStream`.593///594/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](ReflectMeta) method595pub fn get_type_registration(596&self,597where_clause_options: &WhereClauseOptions,598) -> proc_macro2::TokenStream {599crate::registration::impl_get_type_registration(600where_clause_options,601self.serialization_data(),602Some(self.active_types().iter()),603)604}605606/// Get a collection of types which are exposed to the reflection API607pub fn active_types(&self) -> IndexSet<Type> {608// Collect into an `IndexSet` to eliminate duplicate types.609self.active_fields()610.map(|field| field.reflected_type().clone())611.collect::<IndexSet<_>>()612}613614/// Get an iterator of fields which are exposed to the reflection API.615pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {616self.fields()617.iter()618.filter(|field| field.attrs.ignore.is_active())619}620621/// Get an iterator of fields which are ignored by the reflection API622pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {623self.fields()624.iter()625.filter(|field| field.attrs.ignore.is_ignored())626}627628/// The complete set of fields in this struct.629pub fn fields(&self) -> &[StructField<'a>] {630&self.fields631}632633pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {634WhereClauseOptions::new_with_types(self.meta(), self.active_types())635}636637/// Generates a `TokenStream` for `TypeInfo::Struct` or `TypeInfo::TupleStruct` construction.638pub fn to_info_tokens(&self, is_tuple: bool) -> proc_macro2::TokenStream {639let bevy_reflect_path = self.meta().bevy_reflect_path();640641let (info_variant, info_struct) = if is_tuple {642(643Ident::new("TupleStruct", Span::call_site()),644Ident::new("TupleStructInfo", Span::call_site()),645)646} else {647(648Ident::new("Struct", Span::call_site()),649Ident::new("StructInfo", Span::call_site()),650)651};652653let field_infos = self654.active_fields()655.map(|field| field.to_info_tokens(bevy_reflect_path));656657let mut info = quote! {658#bevy_reflect_path::#info_struct::new::<Self>(&[659#(#field_infos),*660])661};662663let custom_attributes = self.meta.attrs.custom_attributes();664if !custom_attributes.is_empty() {665let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);666info.extend(quote! {667.with_custom_attributes(#custom_attributes)668});669}670671if let Some(generics) = generate_generics(self.meta()) {672info.extend(quote! {673.with_generics(#generics)674});675}676677#[cfg(feature = "documentation")]678{679let docs = self.meta().doc();680if !docs.is_empty() {681info.extend(quote! {682.with_docs(#docs)683});684}685}686687quote! {688#bevy_reflect_path::TypeInfo::#info_variant(#info)689}690}691/// Returns the `Reflect::reflect_clone` impl, if any, as a `TokenStream`.692pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {693let bevy_reflect_path = self.meta().bevy_reflect_path();694695if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {696return container_clone;697}698699let mut tokens = proc_macro2::TokenStream::new();700701for field in self.fields().iter() {702let field_ty = field.reflected_type();703let member = field.to_member();704let accessor = self.access_for_field(field, false);705706match &field.attrs.clone {707CloneBehavior::Default => {708let value = if field.attrs.ignore.is_ignored() {709let field_id = field.field_id(bevy_reflect_path);710711quote! {712return #FQResult::Err(#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {713field: #field_id,714variant: #FQOption::None,715container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(716<Self as #bevy_reflect_path::TypePath>::type_path()717)718})719}720} else {721quote! {722<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#accessor)?723}724};725726tokens.extend(quote! {727#member: #value,728});729}730CloneBehavior::Trait => {731tokens.extend(quote! {732#member: #FQClone::clone(#accessor),733});734}735CloneBehavior::Func(clone_fn) => {736tokens.extend(quote! {737#member: #clone_fn(#accessor),738});739}740}741}742743let ctor = match self.meta.remote_ty() {744Some(ty) => {745let ty = ty.as_expr_path().ok()?.to_token_stream();746quote! {747Self(#ty {748#tokens749})750}751}752None => {753quote! {754Self {755#tokens756}757}758}759};760761Some(quote! {762#[inline]763#[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]764fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {765#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#ctor))766}767})768}769770/// Generates an accessor for the given field.771///772/// The mutability of the access can be controlled by the `is_mut` parameter.773///774/// Generally, this just returns something like `&self.field`.775/// However, if the struct is a remote wrapper, this then becomes `&self.0.field` in order to access the field on the inner type.776///777/// If the field itself is a remote type, the above accessor is further wrapped in a call to `ReflectRemote::as_wrapper[_mut]`.778pub fn access_for_field(779&self,780field: &StructField<'a>,781is_mutable: bool,782) -> proc_macro2::TokenStream {783let bevy_reflect_path = self.meta().bevy_reflect_path();784let member = field.to_member();785786let prefix_tokens = if is_mutable { quote!(&mut) } else { quote!(&) };787788let accessor = if self.meta.is_remote_wrapper() {789quote!(self.0.#member)790} else {791quote!(self.#member)792};793794match &field.attrs.remote {795Some(wrapper_ty) => {796let method = if is_mutable {797format_ident!("as_wrapper_mut")798} else {799format_ident!("as_wrapper")800};801802quote! {803<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::#method(#prefix_tokens #accessor)804}805}806None => quote!(#prefix_tokens #accessor),807}808}809}810811impl<'a> ReflectEnum<'a> {812/// Access the metadata associated with this enum definition.813pub fn meta(&self) -> &ReflectMeta<'a> {814&self.meta815}816817/// Returns the given ident as a qualified unit variant of this enum.818///819/// This takes into account the remote type, if any.820pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {821let name = self822.meta823.remote_ty824.map(|path| match path.as_expr_path() {825Ok(path) => path.to_token_stream(),826Err(err) => err.into_compile_error(),827})828.unwrap_or_else(|| self.meta.type_path().to_token_stream());829830quote! {831#name::#variant832}833}834835/// The complete set of variants in this enum.836pub fn variants(&self) -> &[EnumVariant<'a>] {837&self.variants838}839840/// Get a collection of types which are exposed to the reflection API841pub fn active_types(&self) -> IndexSet<Type> {842// Collect into an `IndexSet` to eliminate duplicate types.843self.active_fields()844.map(|field| field.reflected_type().clone())845.collect::<IndexSet<_>>()846}847848/// Get an iterator of fields which are exposed to the reflection API849pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {850self.variants.iter().flat_map(EnumVariant::active_fields)851}852853pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {854WhereClauseOptions::new_with_types(self.meta(), self.active_types())855}856857/// Returns the `GetTypeRegistration` impl as a `TokenStream`.858///859/// Returns a specific implementation for enums and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method860pub fn get_type_registration(861&self,862where_clause_options: &WhereClauseOptions,863) -> proc_macro2::TokenStream {864crate::registration::impl_get_type_registration(865where_clause_options,866None,867Some(self.active_types().iter()),868)869}870871/// Generates a `TokenStream` for `TypeInfo::Enum` construction.872pub fn to_info_tokens(&self) -> proc_macro2::TokenStream {873let bevy_reflect_path = self.meta().bevy_reflect_path();874875let variants = self876.variants877.iter()878.map(|variant| variant.to_info_tokens(bevy_reflect_path));879880let mut info = quote! {881#bevy_reflect_path::EnumInfo::new::<Self>(&[882#(#variants),*883])884};885886let custom_attributes = self.meta.attrs.custom_attributes();887if !custom_attributes.is_empty() {888let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);889info.extend(quote! {890.with_custom_attributes(#custom_attributes)891});892}893894if let Some(generics) = generate_generics(self.meta()) {895info.extend(quote! {896.with_generics(#generics)897});898}899900#[cfg(feature = "documentation")]901{902let docs = self.meta().doc();903if !docs.is_empty() {904info.extend(quote! {905.with_docs(#docs)906});907}908}909910quote! {911#bevy_reflect_path::TypeInfo::Enum(#info)912}913}914915/// Returns the `Reflect::reflect_clone` impl, if any, as a `TokenStream`.916pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {917let bevy_reflect_path = self.meta().bevy_reflect_path();918919if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {920return container_clone;921}922923let this = Ident::new("this", Span::call_site());924let EnumVariantOutputData {925variant_patterns,926variant_constructors,927..928} = ReflectCloneVariantBuilder::new(self).build(&this);929930let inner = quote! {931match #this {932#(#variant_patterns => #variant_constructors),*933}934};935936let body = if variant_patterns.is_empty() {937// enum variant is empty, so &self will never exist938quote!(unreachable!())939} else if self.meta.is_remote_wrapper() {940quote! {941let #this = <Self as #bevy_reflect_path::ReflectRemote>::as_remote(self);942#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(#inner)))943}944} else {945quote! {946let #this = self;947#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#inner))948}949};950951Some(quote! {952#[inline]953#[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]954fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {955#body956}957})958}959}960961impl<'a> EnumVariant<'a> {962/// Get an iterator of fields which are exposed to the reflection API963pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {964self.fields()965.iter()966.filter(|field| field.attrs.ignore.is_active())967}968969/// The complete set of fields in this variant.970pub fn fields(&self) -> &[StructField<'a>] {971match &self.fields {972EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,973EnumVariantFields::Unit => &[],974}975}976977/// Generates a `TokenStream` for `VariantInfo` construction.978pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {979let variant_name = &self.data.ident.to_string();980981let (info_variant, info_struct) = match &self.fields {982EnumVariantFields::Unit => (983Ident::new("Unit", Span::call_site()),984Ident::new("UnitVariantInfo", Span::call_site()),985),986EnumVariantFields::Unnamed(..) => (987Ident::new("Tuple", Span::call_site()),988Ident::new("TupleVariantInfo", Span::call_site()),989),990EnumVariantFields::Named(..) => (991Ident::new("Struct", Span::call_site()),992Ident::new("StructVariantInfo", Span::call_site()),993),994};995996let fields = self997.active_fields()998.map(|field| field.to_info_tokens(bevy_reflect_path));9991000let args = match &self.fields {1001EnumVariantFields::Unit => quote!(#variant_name),1002_ => {1003quote!( #variant_name , &[#(#fields),*] )1004}1005};10061007let mut info = quote! {1008#bevy_reflect_path::#info_struct::new(#args)1009};10101011let custom_attributes = &self.attrs.custom_attributes;1012if !custom_attributes.is_empty() {1013let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);1014info.extend(quote! {1015.with_custom_attributes(#custom_attributes)1016});1017}10181019#[cfg(feature = "documentation")]1020{1021let docs = &self.doc;1022if !docs.is_empty() {1023info.extend(quote! {1024.with_docs(#docs)1025});1026}1027}10281029quote! {1030#bevy_reflect_path::VariantInfo::#info_variant(#info)1031}1032}1033}10341035/// Represents a path to a type.1036///1037/// This is used over [`struct@Ident`] or [`Path`]1038/// to have the correct semantics for [deriving `TypePath`].1039///1040/// The type can always be reached with its [`ToTokens`] implementation.1041///1042/// The [`short_type_path`], [`type_ident`], [`crate_name`], and [`module_path`] methods1043/// have corresponding methods on the `TypePath` trait.1044/// [`long_type_path`] corresponds to the `type_path` method on `TypePath`.1045///1046/// [deriving `TypePath`]: crate::derive_type_path1047/// [`long_type_path`]: ReflectTypePath::long_type_path1048/// [`short_type_path`]: ReflectTypePath::short_type_path1049/// [`type_ident`]: ReflectTypePath::type_ident1050/// [`crate_name`]: ReflectTypePath::crate_name1051/// [`module_path`]: ReflectTypePath::module_path1052///1053/// # Example1054///1055/// ```ignore (bevy_reflect is not accessible from this crate)1056/// # use syn::parse_quote;1057/// # use bevy_reflect_derive::ReflectTypePath;1058/// let path: syn::Path = parse_quote!(::std::marker::PhantomData)?;1059///1060/// let type_path = ReflectTypePath::External {1061/// path,1062/// custom_path: None,1063/// };1064///1065/// // Equivalent to "std::marker".1066/// let module_path = type_path.module_path();1067/// # Ok::<(), syn::Error>(())1068/// ```1069pub(crate) enum ReflectTypePath<'a> {1070/// Types without a crate/module that can be named from any scope (e.g. `bool`).1071Primitive(&'a Ident),1072/// Using `::my_crate::foo::Bar` syntax.1073///1074/// May have a separate custom path used for the `TypePath` implementation.1075External {1076path: &'a Path,1077custom_path: Option<Path>,1078generics: &'a Generics,1079},1080/// The name of a type relative to its scope.1081///1082/// The type must be able to be reached with just its name.1083///1084/// May have a separate alias path used for the `TypePath` implementation.1085///1086/// Module and crate are found with [`module_path!()`](module_path),1087/// if there is no custom path specified.1088Internal {1089ident: &'a Ident,1090custom_path: Option<Path>,1091generics: &'a Generics,1092},1093/// Any [`Type`] with only a defined `type_path` and `short_type_path`.1094#[expect(1095dead_code,1096reason = "Not currently used but may be useful in the future due to its generality."1097)]1098Anonymous {1099qualified_type: Box<Type>,1100long_type_path: StringExpr,1101short_type_path: StringExpr,1102},1103}11041105impl<'a> ReflectTypePath<'a> {1106/// Returns the path interpreted as an [`struct@Ident`].1107///1108/// Returns [`None`] if [anonymous].1109///1110/// [anonymous]: ReflectTypePath::Anonymous1111pub fn get_ident(&self) -> Option<&Ident> {1112match self {1113Self::Internal {1114ident, custom_path, ..1115} => Some(1116custom_path1117.as_ref()1118.map(|path| &path.segments.last().unwrap().ident)1119.unwrap_or(ident),1120),1121Self::External {1122path, custom_path, ..1123} => Some(1124&custom_path1125.as_ref()1126.unwrap_or(path)1127.segments1128.last()1129.unwrap()1130.ident,1131),1132Self::Primitive(ident) => Some(ident),1133_ => None,1134}1135}11361137/// The generics associated with the type.1138///1139/// Empty if [anonymous] or [primitive].1140///1141/// [primitive]: ReflectTypePath::Primitive1142/// [anonymous]: ReflectTypePath::Anonymous1143pub fn generics(&self) -> &'a Generics {1144// Use a constant because we need to return a reference of at least 'a.1145const EMPTY_GENERICS: &Generics = &Generics {1146gt_token: None,1147lt_token: None,1148where_clause: None,1149params: Punctuated::new(),1150};11511152match self {1153Self::Internal { generics, .. } | Self::External { generics, .. } => generics,1154_ => EMPTY_GENERICS,1155}1156}11571158/// Whether an implementation of `Typed` or `TypePath` should be generic.1159///1160/// Returning true that it should use a `GenericTypeCell` in its implementation.1161pub fn impl_is_generic(&self) -> bool {1162// Whether to use `GenericTypeCell` is not dependent on lifetimes1163// (which all have to be 'static anyway).1164!self1165.generics()1166.params1167.iter()1168.all(|param| matches!(param, GenericParam::Lifetime(_)))1169}11701171/// Returns the path interpreted as a [`Path`].1172///1173/// Returns [`None`] if [anonymous], [primitive],1174/// or a [`ReflectTypePath::Internal`] without a custom path.1175///1176/// [primitive]: ReflectTypePath::Primitive1177/// [anonymous]: ReflectTypePath::Anonymous1178pub fn get_path(&self) -> Option<&Path> {1179match self {1180Self::Internal { custom_path, .. } => custom_path.as_ref(),1181Self::External {1182path, custom_path, ..1183} => Some(custom_path.as_ref().unwrap_or(path)),1184_ => None,1185}1186}11871188/// Returns whether this [internal] or [external] path has a custom path.1189///1190/// [internal]: ReflectTypePath::Internal1191/// [external]: ReflectTypePath::External1192pub fn has_custom_path(&self) -> bool {1193match self {1194Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {1195custom_path.is_some()1196}1197_ => false,1198}1199}12001201/// Returns a [`StringExpr`] representing the name of the type's crate.1202///1203/// Returns [`None`] if the type is [primitive] or [anonymous].1204///1205/// For non-customized [internal] paths this is created from [`module_path`].1206///1207/// For `Option<PhantomData>`, this is `"core"`.1208///1209/// [primitive]: ReflectTypePath::Primitive1210/// [anonymous]: ReflectTypePath::Anonymous1211/// [internal]: ReflectTypePath::Internal1212pub fn crate_name(&self) -> Option<StringExpr> {1213if let Some(path) = self.get_path() {1214let crate_name = &path.segments.first().unwrap().ident;1215return Some(StringExpr::from(crate_name));1216}12171218match self {1219Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {1220::core::module_path!()1221.split(':')1222.next()1223.unwrap()1224})),1225Self::External { .. } => unreachable!(),1226_ => None,1227}1228}12291230/// Combines type generics and const generics into one [`StringExpr`].1231///1232/// This string can be used with a `GenericTypePathCell` in a `TypePath` implementation.1233///1234/// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s.1235fn reduce_generics(1236generics: &Generics,1237mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,1238bevy_reflect_path: &Path,1239) -> StringExpr {1240let mut params = generics.params.iter().filter_map(|param| match param {1241GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),1242GenericParam::Const(const_param) => {1243let ident = &const_param.ident;1244let ty = &const_param.ty;12451246Some(StringExpr::Owned(quote! {1247<#ty as #bevy_reflect_path::__macro_exports::alloc_utils::ToString>::to_string(&#ident)1248}))1249}1250GenericParam::Lifetime(_) => None,1251});12521253params1254.next()1255.into_iter()1256.chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))1257.collect()1258}12591260/// Returns a [`StringExpr`] representing the "type path" of the type.1261///1262/// For `Option<PhantomData>`, this is `"std::option::Option<std::marker::PhantomData>"`.1263pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {1264match self {1265Self::Primitive(ident) => StringExpr::from(ident),1266Self::Anonymous { long_type_path, .. } => long_type_path.clone(),1267Self::Internal { generics, .. } | Self::External { generics, .. } => {1268let ident = self.type_ident().unwrap();1269let module_path = self.module_path().unwrap();12701271if self.impl_is_generic() {1272let generics = ReflectTypePath::reduce_generics(1273generics,1274|TypeParam { ident, .. }| {1275StringExpr::Borrowed(quote! {1276<#ident as #bevy_reflect_path::TypePath>::type_path()1277})1278},1279bevy_reflect_path,1280);12811282StringExpr::from_iter([1283module_path,1284StringExpr::from_str("::"),1285ident,1286StringExpr::from_str("<"),1287generics,1288StringExpr::from_str(">"),1289])1290} else {1291StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])1292}1293}1294}1295}12961297/// Returns a [`StringExpr`] representing the "short path" of the type.1298///1299/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.1300pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {1301match self {1302Self::Anonymous {1303short_type_path, ..1304} => short_type_path.clone(),1305Self::Primitive(ident) => StringExpr::from(ident),1306Self::External { generics, .. } | Self::Internal { generics, .. } => {1307let ident = self.type_ident().unwrap();13081309if self.impl_is_generic() {1310let generics = ReflectTypePath::reduce_generics(1311generics,1312|TypeParam { ident, .. }| {1313StringExpr::Borrowed(quote! {1314<#ident as #bevy_reflect_path::TypePath>::short_type_path()1315})1316},1317bevy_reflect_path,1318);13191320StringExpr::from_iter([1321ident,1322StringExpr::from_str("<"),1323generics,1324StringExpr::from_str(">"),1325])1326} else {1327ident1328}1329}1330}1331}13321333/// Returns a [`StringExpr`] representing the path to the module1334/// that the type is in.1335///1336/// Returns [`None`] if the type is [primitive] or [anonymous].1337///1338/// For non-customized [internal] paths this is created from [`module_path`].1339///1340/// For `Option<PhantomData>`, this is `"std::option"`.1341///1342/// [primitive]: ReflectTypePath::Primitive1343/// [anonymous]: ReflectTypePath::Anonymous1344/// [internal]: ReflectTypePath::Internal1345pub fn module_path(&self) -> Option<StringExpr> {1346if let Some(path) = self.get_path() {1347let path_string = path1348.segments1349.pairs()1350.take(path.segments.len() - 1)1351.map(|pair| pair.value().ident.to_string())1352.reduce(|path, ident| path + "::" + &ident)1353.unwrap();13541355let path_lit = LitStr::new(&path_string, path.span());1356return Some(StringExpr::from_lit(&path_lit));1357}13581359match self {1360Self::Internal { .. } => Some(StringExpr::Const(quote! {1361::core::module_path!()1362})),1363Self::External { .. } => unreachable!(),1364_ => None,1365}1366}13671368/// Returns a [`StringExpr`] representing the type's final ident.1369///1370/// Returns [`None`] if the type is [anonymous].1371///1372/// This is not necessarily a valid qualified path to the type.1373///1374/// For `Option<PhantomData>`, this is `"Option"`.1375///1376/// [anonymous]: ReflectTypePath::Anonymous1377pub fn type_ident(&self) -> Option<StringExpr> {1378self.get_ident().map(StringExpr::from)1379}13801381/// Returns the true type regardless of whether a custom path is specified.1382///1383/// To get the custom path if there is one, use [`Self::get_path`].1384///1385/// For example, the type `Foo<T: Debug>` would return `Foo<T>`.1386pub fn true_type(&self) -> proc_macro2::TokenStream {1387match self {1388Self::Primitive(ident) => quote!(#ident),1389Self::Internal {1390ident, generics, ..1391} => {1392let (_, ty_generics, _) = generics.split_for_impl();1393quote!(#ident #ty_generics)1394}1395Self::External { path, generics, .. } => {1396let (_, ty_generics, _) = generics.split_for_impl();1397quote!(#path #ty_generics)1398}1399Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),1400}1401}1402}14031404impl<'a> ToTokens for ReflectTypePath<'a> {1405fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {1406match self {1407Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),1408Self::External { path, .. } => path.to_tokens(tokens),1409Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),1410}1411}1412}141314141415