Path: blob/main/crates/bevy_reflect/derive/src/derive_data.rs
9421 views
use core::fmt;1use indexmap::IndexSet;2use proc_macro2::Span;34use crate::{5container_attributes::{ContainerAttributes, FromReflectAttrs, TypePathAttrs},6field_attributes::FieldAttributes,7remote::RemoteType,8serialization::SerializationDataDef,9string_expr::StringExpr,10type_path::parse_path_no_leading_colon,11where_clause_options::WhereClauseOptions,12REFLECT_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME, TYPE_PATH_ATTRIBUTE_NAME,13};14use bevy_macro_utils::ResultSifter;15use quote::{format_ident, quote, ToTokens};16use syn::{token::Comma, MacroDelimiter};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 = "reflect_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 = "reflect_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 = "reflect_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 = "reflect_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) => {199if let MacroDelimiter::Paren(_) = meta_list.delimiter {200container_attributes.parse_meta_list(meta_list, provenance.trait_)?;201} else {202return Err(syn::Error::new(203meta_list.delimiter.span().join(),204format_args!(205"`#[{REFLECT_ATTRIBUTE_NAME}(\"...\")]` must use parentheses `(` and `)`"206),207));208}209}210Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {211let syn::Expr::Lit(syn::ExprLit {212lit: syn::Lit::Str(lit),213..214}) = &pair.value215else {216return Err(syn::Error::new(217pair.span(),218format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),219));220};221222custom_path = Some(syn::parse::Parser::parse_str(223parse_path_no_leading_colon,224&lit.value(),225)?);226}227Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {228let syn::Expr::Lit(syn::ExprLit {229lit: syn::Lit::Str(lit),230..231}) = &pair.value232else {233return Err(syn::Error::new(234pair.span(),235format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),236));237};238239custom_type_name = Some(parse_str(&lit.value())?);240}241#[cfg(feature = "reflect_documentation")]242Meta::NameValue(pair) if pair.path.is_ident("doc") => {243if let syn::Expr::Lit(syn::ExprLit {244lit: syn::Lit::Str(lit),245..246}) = &pair.value247{248doc.push(lit.value());249}250}251_ => continue,252}253}254match (&mut custom_path, custom_type_name) {255(Some(path), custom_type_name) => {256let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());257path.segments.push(PathSegment::from(ident));258}259(None, Some(name)) => {260return Err(syn::Error::new(261name.span(),262format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),263));264}265_ => (),266}267268let type_path = ReflectTypePath::Internal {269ident: &input.ident,270custom_path,271generics: &input.generics,272};273274let meta = ReflectMeta::new(type_path, container_attributes);275276if provenance.source == ReflectImplSource::ImplRemoteType277&& meta.type_path_attrs().should_auto_derive()278&& !meta.type_path().has_custom_path()279{280return Err(syn::Error::new(281meta.type_path().span(),282format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}"),283));284}285286#[cfg(feature = "reflect_documentation")]287let meta = meta.with_docs(doc);288289if meta.attrs().is_opaque() {290return Ok(Self::Opaque(meta));291}292293match &input.data {294Data::Struct(data) => {295let fields = Self::collect_struct_fields(&data.fields)?;296let serialization_data =297SerializationDataDef::new(&fields, &meta.bevy_reflect_path)?;298let reflect_struct = ReflectStruct {299meta,300serialization_data,301fields,302};303304match data.fields {305Fields::Named(..) => Ok(Self::Struct(reflect_struct)),306Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)),307Fields::Unit => Ok(Self::UnitStruct(reflect_struct)),308}309}310Data::Enum(data) => {311let variants = Self::collect_enum_variants(&data.variants)?;312313let reflect_enum = ReflectEnum { meta, variants };314Ok(Self::Enum(reflect_enum))315}316Data::Union(..) => Err(syn::Error::new(317input.span(),318"reflection not supported for unions",319)),320}321}322323/// Set the remote type for this derived type.324///325/// # Panics326///327/// Panics when called on [`ReflectDerive::Opaque`].328pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {329match self {330Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {331data.meta.remote_ty = remote_ty;332}333Self::Enum(data) => {334data.meta.remote_ty = remote_ty;335}336Self::Opaque(meta) => {337meta.remote_ty = remote_ty;338}339}340}341342/// Get the remote type path, if any.343pub fn remote_ty(&self) -> Option<RemoteType<'_>> {344match self {345Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {346data.meta.remote_ty()347}348Self::Enum(data) => data.meta.remote_ty(),349Self::Opaque(meta) => meta.remote_ty(),350}351}352353/// Get the [`ReflectMeta`] for this derived type.354pub fn meta(&self) -> &ReflectMeta<'_> {355match self {356Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),357Self::Enum(data) => data.meta(),358Self::Opaque(meta) => meta,359}360}361362pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {363match self {364Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {365data.where_clause_options()366}367Self::Enum(data) => data.where_clause_options(),368Self::Opaque(meta) => WhereClauseOptions::new(meta),369}370}371372fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {373let mut active_index = 0;374let sifter: ResultSifter<StructField<'a>> = fields375.iter()376.enumerate()377.map(378|(declaration_index, field)| -> Result<StructField, syn::Error> {379let attrs = FieldAttributes::parse_attributes(&field.attrs)?;380381let reflection_index = if attrs.ignore.is_ignored() {382None383} else {384active_index += 1;385Some(active_index - 1)386};387388Ok(StructField {389declaration_index,390reflection_index,391attrs,392data: field,393#[cfg(feature = "reflect_documentation")]394doc: crate::documentation::Documentation::from_attributes(&field.attrs),395})396},397)398.fold(ResultSifter::default(), ResultSifter::fold);399400sifter.finish()401}402403fn collect_enum_variants(404variants: &'a Punctuated<Variant, Comma>,405) -> Result<Vec<EnumVariant<'a>>, syn::Error> {406let sifter: ResultSifter<EnumVariant<'a>> = variants407.iter()408.map(|variant| -> Result<EnumVariant, syn::Error> {409let fields = Self::collect_struct_fields(&variant.fields)?;410411let fields = match variant.fields {412Fields::Named(..) => EnumVariantFields::Named(fields),413Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),414Fields::Unit => EnumVariantFields::Unit,415};416Ok(EnumVariant {417fields,418attrs: FieldAttributes::parse_attributes(&variant.attrs)?,419data: variant,420#[cfg(feature = "reflect_documentation")]421doc: crate::documentation::Documentation::from_attributes(&variant.attrs),422})423})424.fold(ResultSifter::default(), ResultSifter::fold);425426sifter.finish()427}428}429430impl<'a> ReflectMeta<'a> {431pub fn new(type_path: ReflectTypePath<'a>, attrs: ContainerAttributes) -> Self {432Self {433attrs,434type_path,435remote_ty: None,436bevy_reflect_path: crate::meta::get_bevy_reflect_path(),437#[cfg(feature = "reflect_documentation")]438docs: Default::default(),439}440}441442/// Sets the documentation for this type.443#[cfg(feature = "reflect_documentation")]444pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {445Self { docs, ..self }446}447448/// The registered reflect attributes on this struct.449pub fn attrs(&self) -> &ContainerAttributes {450&self.attrs451}452453/// The `FromReflect` attributes on this type.454#[expect(455clippy::wrong_self_convention,456reason = "Method returns `FromReflectAttrs`, does not actually convert data."457)]458pub fn from_reflect(&self) -> &FromReflectAttrs {459self.attrs.from_reflect_attrs()460}461462/// The `TypePath` attributes on this type.463pub fn type_path_attrs(&self) -> &TypePathAttrs {464self.attrs.type_path_attrs()465}466467/// The path to this type.468pub fn type_path(&self) -> &ReflectTypePath<'a> {469&self.type_path470}471472/// Get the remote type path, if any.473pub fn remote_ty(&self) -> Option<RemoteType<'_>> {474self.remote_ty475}476477/// Whether this reflected type represents a remote type or not.478pub fn is_remote_wrapper(&self) -> bool {479self.remote_ty.is_some()480}481482/// The cached `bevy_reflect` path.483pub fn bevy_reflect_path(&self) -> &Path {484&self.bevy_reflect_path485}486487/// Returns the `GetTypeRegistration` impl as a `TokenStream`.488pub fn get_type_registration(489&self,490where_clause_options: &WhereClauseOptions,491) -> proc_macro2::TokenStream {492crate::registration::impl_get_type_registration(493where_clause_options,494None,495Option::<core::iter::Empty<&Type>>::None,496)497}498499/// The collection of docstrings for this type, if any.500#[cfg(feature = "reflect_documentation")]501pub fn doc(&self) -> &crate::documentation::Documentation {502&self.docs503}504}505506impl<'a> StructField<'a> {507/// Generates a `TokenStream` for `NamedField` or `UnnamedField` construction.508pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {509let name = match &self.data.ident {510Some(ident) => ident.to_string().to_token_stream(),511None => self.reflection_index.to_token_stream(),512};513514let field_info = if self.data.ident.is_some() {515quote! {516#bevy_reflect_path::NamedField517}518} else {519quote! {520#bevy_reflect_path::UnnamedField521}522};523524let ty = self.reflected_type();525526let mut info = quote! {527#field_info::new::<#ty>(#name)528};529530let custom_attributes = &self.attrs.custom_attributes;531if !custom_attributes.is_empty() {532let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);533info.extend(quote! {534.with_custom_attributes(#custom_attributes)535});536}537538#[cfg(feature = "reflect_documentation")]539{540let docs = &self.doc;541if !docs.is_empty() {542info.extend(quote! {543.with_docs(#docs)544});545}546}547548info549}550551/// Returns the reflected type of this field.552///553/// Normally this is just the field's defined type.554/// However, this can be adjusted to use a different type, like for representing remote types.555/// In those cases, the returned value is the remote wrapper type.556pub fn reflected_type(&self) -> &Type {557self.attrs.remote.as_ref().unwrap_or(&self.data.ty)558}559560pub fn attrs(&self) -> &FieldAttributes {561&self.attrs562}563564/// Generates a [`Member`] based on this field.565///566/// If the field is unnamed, the declaration index is used.567/// This allows this member to be used for both active and ignored fields.568pub fn to_member(&self) -> Member {569match &self.data.ident {570Some(ident) => Member::Named(ident.clone()),571None => Member::Unnamed(self.declaration_index.into()),572}573}574575/// Returns a token stream for generating a `FieldId` for this field.576pub fn field_id(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {577match &self.data.ident {578Some(ident) => {579let name = ident.to_string();580quote!(#bevy_reflect_path::FieldId::Named(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#name)))581}582None => {583let index = self.declaration_index;584quote!(#bevy_reflect_path::FieldId::Unnamed(#index))585}586}587}588}589590impl<'a> ReflectStruct<'a> {591/// Access the metadata associated with this struct definition.592pub fn meta(&self) -> &ReflectMeta<'a> {593&self.meta594}595596/// Returns the [`SerializationDataDef`] for this struct.597pub fn serialization_data(&self) -> Option<&SerializationDataDef> {598self.serialization_data.as_ref()599}600601/// Returns the `GetTypeRegistration` impl as a `TokenStream`.602///603/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](ReflectMeta) method604pub fn get_type_registration(605&self,606where_clause_options: &WhereClauseOptions,607) -> proc_macro2::TokenStream {608crate::registration::impl_get_type_registration(609where_clause_options,610self.serialization_data(),611Some(self.active_types().iter()),612)613}614615/// Get a collection of types which are exposed to the reflection API616pub fn active_types(&self) -> IndexSet<Type> {617// Collect into an `IndexSet` to eliminate duplicate types.618self.active_fields()619.map(|field| field.reflected_type().clone())620.collect::<IndexSet<_>>()621}622623/// Get an iterator of fields which are exposed to the reflection API.624pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {625self.fields()626.iter()627.filter(|field| field.attrs.ignore.is_active())628}629630/// Get an iterator of fields which are ignored by the reflection API631pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {632self.fields()633.iter()634.filter(|field| field.attrs.ignore.is_ignored())635}636637/// The complete set of fields in this struct.638pub fn fields(&self) -> &[StructField<'a>] {639&self.fields640}641642pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {643WhereClauseOptions::new_with_types(self.meta(), self.active_types())644}645646/// Generates a `TokenStream` for `TypeInfo::Struct` or `TypeInfo::TupleStruct` construction.647pub fn to_info_tokens(&self, is_tuple: bool) -> proc_macro2::TokenStream {648let bevy_reflect_path = self.meta().bevy_reflect_path();649650let (info_variant, info_struct): (_, Path) = if is_tuple {651(652Ident::new("TupleStruct", Span::call_site()),653parse_str("tuple_struct::TupleStructInfo").expect("should be a valid path"),654)655} else {656(657Ident::new("Struct", Span::call_site()),658parse_str("structs::StructInfo").expect("should be a valid path"),659)660};661662let field_infos = self663.active_fields()664.map(|field| field.to_info_tokens(bevy_reflect_path));665666let mut info = quote! {667#bevy_reflect_path::#info_struct::new::<Self>(&[668#(#field_infos),*669])670};671672let custom_attributes = self.meta.attrs.custom_attributes();673if !custom_attributes.is_empty() {674let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);675info.extend(quote! {676.with_custom_attributes(#custom_attributes)677});678}679680if let Some(generics) = generate_generics(self.meta()) {681info.extend(quote! {682.with_generics(#generics)683});684}685686#[cfg(feature = "reflect_documentation")]687{688let docs = self.meta().doc();689if !docs.is_empty() {690info.extend(quote! {691.with_docs(#docs)692});693}694}695696quote! {697#bevy_reflect_path::TypeInfo::#info_variant(#info)698}699}700/// Returns the `Reflect::reflect_clone` impl, if any, as a `TokenStream`.701pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {702let bevy_reflect_path = self.meta().bevy_reflect_path();703704if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {705return container_clone;706}707708let mut tokens = proc_macro2::TokenStream::new();709710for field in self.fields().iter() {711let field_ty = field.reflected_type();712let member = field.to_member();713let accessor = self.access_for_field(field, false);714715match &field.attrs.clone {716CloneBehavior::Default => {717let value = if field.attrs.ignore.is_ignored() {718let field_id = field.field_id(bevy_reflect_path);719720quote! {721return #FQResult::Err(#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {722field: #field_id,723variant: #FQOption::None,724container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(725<Self as #bevy_reflect_path::TypePath>::type_path()726)727})728}729} else {730quote! {731<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#accessor)?732}733};734735tokens.extend(quote! {736#member: #value,737});738}739CloneBehavior::Trait => {740tokens.extend(quote! {741#member: #FQClone::clone(#accessor),742});743}744CloneBehavior::Func(clone_fn) => {745tokens.extend(quote! {746#member: #clone_fn(#accessor),747});748}749}750}751752let ctor = match self.meta.remote_ty() {753Some(ty) => {754let ty = ty.as_expr_path().ok()?.to_token_stream();755quote! {756Self(#ty {757#tokens758})759}760}761None => {762quote! {763Self {764#tokens765}766}767}768};769770Some(quote! {771#[inline]772#[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]773fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {774#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#ctor))775}776})777}778779/// Generates an accessor for the given field.780///781/// The mutability of the access can be controlled by the `is_mut` parameter.782///783/// Generally, this just returns something like `&self.field`.784/// However, if the struct is a remote wrapper, this then becomes `&self.0.field` in order to access the field on the inner type.785///786/// If the field itself is a remote type, the above accessor is further wrapped in a call to `ReflectRemote::as_wrapper[_mut]`.787pub fn access_for_field(788&self,789field: &StructField<'a>,790is_mutable: bool,791) -> proc_macro2::TokenStream {792let bevy_reflect_path = self.meta().bevy_reflect_path();793let member = field.to_member();794795let prefix_tokens = if is_mutable { quote!(&mut) } else { quote!(&) };796797let accessor = if self.meta.is_remote_wrapper() {798quote!(self.0.#member)799} else {800quote!(self.#member)801};802803match &field.attrs.remote {804Some(wrapper_ty) => {805let method = if is_mutable {806format_ident!("as_wrapper_mut")807} else {808format_ident!("as_wrapper")809};810811quote! {812<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::#method(#prefix_tokens #accessor)813}814}815None => quote!(#prefix_tokens #accessor),816}817}818}819820impl<'a> ReflectEnum<'a> {821/// Access the metadata associated with this enum definition.822pub fn meta(&self) -> &ReflectMeta<'a> {823&self.meta824}825826/// Returns the given ident as a qualified unit variant of this enum.827///828/// This takes into account the remote type, if any.829pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {830let name = self831.meta832.remote_ty833.map(|path| match path.as_expr_path() {834Ok(path) => path.to_token_stream(),835Err(err) => err.into_compile_error(),836})837.unwrap_or_else(|| self.meta.type_path().to_token_stream());838839quote! {840#name::#variant841}842}843844/// The complete set of variants in this enum.845pub fn variants(&self) -> &[EnumVariant<'a>] {846&self.variants847}848849/// Get a collection of types which are exposed to the reflection API850pub fn active_types(&self) -> IndexSet<Type> {851// Collect into an `IndexSet` to eliminate duplicate types.852self.active_fields()853.map(|field| field.reflected_type().clone())854.collect::<IndexSet<_>>()855}856857/// Get an iterator of fields which are exposed to the reflection API858pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {859self.variants.iter().flat_map(EnumVariant::active_fields)860}861862pub fn where_clause_options(&self) -> WhereClauseOptions<'_, '_> {863WhereClauseOptions::new_with_types(self.meta(), self.active_types())864}865866/// Returns the `GetTypeRegistration` impl as a `TokenStream`.867///868/// Returns a specific implementation for enums and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method869pub fn get_type_registration(870&self,871where_clause_options: &WhereClauseOptions,872) -> proc_macro2::TokenStream {873crate::registration::impl_get_type_registration(874where_clause_options,875None,876Some(self.active_types().iter()),877)878}879880/// Generates a `TokenStream` for `TypeInfo::Enum` construction.881pub fn to_info_tokens(&self) -> proc_macro2::TokenStream {882let bevy_reflect_path = self.meta().bevy_reflect_path();883884let variants = self885.variants886.iter()887.map(|variant| variant.to_info_tokens(bevy_reflect_path));888889let mut info = quote! {890#bevy_reflect_path::enums::EnumInfo::new::<Self>(&[891#(#variants),*892])893};894895let custom_attributes = self.meta.attrs.custom_attributes();896if !custom_attributes.is_empty() {897let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);898info.extend(quote! {899.with_custom_attributes(#custom_attributes)900});901}902903if let Some(generics) = generate_generics(self.meta()) {904info.extend(quote! {905.with_generics(#generics)906});907}908909#[cfg(feature = "reflect_documentation")]910{911let docs = self.meta().doc();912if !docs.is_empty() {913info.extend(quote! {914.with_docs(#docs)915});916}917}918919quote! {920#bevy_reflect_path::TypeInfo::Enum(#info)921}922}923924/// Returns the `Reflect::reflect_clone` impl, if any, as a `TokenStream`.925pub fn get_clone_impl(&self) -> Option<proc_macro2::TokenStream> {926let bevy_reflect_path = self.meta().bevy_reflect_path();927928if let container_clone @ Some(_) = self.meta().attrs().get_clone_impl(bevy_reflect_path) {929return container_clone;930}931932let this = Ident::new("this", Span::call_site());933let EnumVariantOutputData {934variant_patterns,935variant_constructors,936..937} = ReflectCloneVariantBuilder::new(self).build(&this);938939let inner = quote! {940match #this {941#(#variant_patterns => #variant_constructors),*942}943};944945let body = if variant_patterns.is_empty() {946// enum variant is empty, so &self will never exist947quote!(unreachable!())948} else if self.meta.is_remote_wrapper() {949quote! {950let #this = <Self as #bevy_reflect_path::ReflectRemote>::as_remote(self);951#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(#inner)))952}953} else {954quote! {955let #this = self;956#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#inner))957}958};959960Some(quote! {961#[inline]962#[allow(unreachable_code, reason = "Ignored fields without a `clone` attribute will early-return with an error")]963fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {964#body965}966})967}968}969970impl<'a> EnumVariant<'a> {971/// Get an iterator of fields which are exposed to the reflection API972pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {973self.fields()974.iter()975.filter(|field| field.attrs.ignore.is_active())976}977978/// The complete set of fields in this variant.979pub fn fields(&self) -> &[StructField<'a>] {980match &self.fields {981EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,982EnumVariantFields::Unit => &[],983}984}985986/// Generates a `TokenStream` for `VariantInfo` construction.987pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {988let variant_name = &self.data.ident.to_string();989990let (info_variant, info_struct) = match &self.fields {991EnumVariantFields::Unit => (992Ident::new("Unit", Span::call_site()),993Ident::new("UnitVariantInfo", Span::call_site()),994),995EnumVariantFields::Unnamed(..) => (996Ident::new("Tuple", Span::call_site()),997Ident::new("TupleVariantInfo", Span::call_site()),998),999EnumVariantFields::Named(..) => (1000Ident::new("Struct", Span::call_site()),1001Ident::new("StructVariantInfo", Span::call_site()),1002),1003};10041005let fields = self1006.active_fields()1007.map(|field| field.to_info_tokens(bevy_reflect_path));10081009let args = match &self.fields {1010EnumVariantFields::Unit => quote!(#variant_name),1011_ => {1012quote!( #variant_name , &[#(#fields),*] )1013}1014};10151016let mut info = quote! {1017#bevy_reflect_path::enums::#info_struct::new(#args)1018};10191020let custom_attributes = &self.attrs.custom_attributes;1021if !custom_attributes.is_empty() {1022let custom_attributes = custom_attributes.to_tokens(bevy_reflect_path);1023info.extend(quote! {1024.with_custom_attributes(#custom_attributes)1025});1026}10271028#[cfg(feature = "reflect_documentation")]1029{1030let docs = &self.doc;1031if !docs.is_empty() {1032info.extend(quote! {1033.with_docs(#docs)1034});1035}1036}10371038quote! {1039#bevy_reflect_path::enums::VariantInfo::#info_variant(#info)1040}1041}1042}10431044/// Represents a path to a type.1045///1046/// This is used over [`struct@Ident`] or [`Path`]1047/// to have the correct semantics for [deriving `TypePath`].1048///1049/// The type can always be reached with its [`ToTokens`] implementation.1050///1051/// The [`short_type_path`], [`type_ident`], [`crate_name`], and [`module_path`] methods1052/// have corresponding methods on the `TypePath` trait.1053/// [`long_type_path`] corresponds to the `type_path` method on `TypePath`.1054///1055/// [deriving `TypePath`]: crate::derive_type_path1056/// [`long_type_path`]: ReflectTypePath::long_type_path1057/// [`short_type_path`]: ReflectTypePath::short_type_path1058/// [`type_ident`]: ReflectTypePath::type_ident1059/// [`crate_name`]: ReflectTypePath::crate_name1060/// [`module_path`]: ReflectTypePath::module_path1061///1062/// # Example1063///1064/// ```ignore (bevy_reflect is not accessible from this crate)1065/// # use syn::parse_quote;1066/// # use bevy_reflect_derive::ReflectTypePath;1067/// let path: syn::Path = parse_quote!(::std::marker::PhantomData)?;1068///1069/// let type_path = ReflectTypePath::External {1070/// path,1071/// custom_path: None,1072/// };1073///1074/// // Equivalent to "std::marker".1075/// let module_path = type_path.module_path();1076/// # Ok::<(), syn::Error>(())1077/// ```1078pub(crate) enum ReflectTypePath<'a> {1079/// Types without a crate/module that can be named from any scope (e.g. `bool`).1080Primitive(&'a Ident),1081/// Using `::my_crate::foo::Bar` syntax.1082///1083/// May have a separate custom path used for the `TypePath` implementation.1084External {1085path: &'a Path,1086custom_path: Option<Path>,1087generics: &'a Generics,1088},1089/// The name of a type relative to its scope.1090///1091/// The type must be able to be reached with just its name.1092///1093/// May have a separate alias path used for the `TypePath` implementation.1094///1095/// Module and crate are found with [`module_path!()`](module_path),1096/// if there is no custom path specified.1097Internal {1098ident: &'a Ident,1099custom_path: Option<Path>,1100generics: &'a Generics,1101},1102/// Any [`Type`] with only a defined `type_path` and `short_type_path`.1103#[expect(1104dead_code,1105reason = "Not currently used but may be useful in the future due to its generality."1106)]1107Anonymous {1108qualified_type: Box<Type>,1109long_type_path: StringExpr,1110short_type_path: StringExpr,1111},1112}11131114impl<'a> ReflectTypePath<'a> {1115/// Returns the path interpreted as an [`struct@Ident`].1116///1117/// Returns [`None`] if [anonymous].1118///1119/// [anonymous]: ReflectTypePath::Anonymous1120pub fn get_ident(&self) -> Option<&Ident> {1121match self {1122Self::Internal {1123ident, custom_path, ..1124} => Some(1125custom_path1126.as_ref()1127.map(|path| &path.segments.last().unwrap().ident)1128.unwrap_or(ident),1129),1130Self::External {1131path, custom_path, ..1132} => Some(1133&custom_path1134.as_ref()1135.unwrap_or(path)1136.segments1137.last()1138.unwrap()1139.ident,1140),1141Self::Primitive(ident) => Some(ident),1142_ => None,1143}1144}11451146/// The generics associated with the type.1147///1148/// Empty if [anonymous] or [primitive].1149///1150/// [primitive]: ReflectTypePath::Primitive1151/// [anonymous]: ReflectTypePath::Anonymous1152pub fn generics(&self) -> &'a Generics {1153// Use a constant because we need to return a reference of at least 'a.1154const EMPTY_GENERICS: &Generics = &Generics {1155gt_token: None,1156lt_token: None,1157where_clause: None,1158params: Punctuated::new(),1159};11601161match self {1162Self::Internal { generics, .. } | Self::External { generics, .. } => generics,1163_ => EMPTY_GENERICS,1164}1165}11661167/// Whether an implementation of `Typed` or `TypePath` should be generic.1168///1169/// Returning true that it should use a `GenericTypeCell` in its implementation.1170pub fn impl_is_generic(&self) -> bool {1171// Whether to use `GenericTypeCell` is not dependent on lifetimes1172// (which all have to be 'static anyway).1173!self1174.generics()1175.params1176.iter()1177.all(|param| matches!(param, GenericParam::Lifetime(_)))1178}11791180/// Returns the path interpreted as a [`Path`].1181///1182/// Returns [`None`] if [anonymous], [primitive],1183/// or a [`ReflectTypePath::Internal`] without a custom path.1184///1185/// [primitive]: ReflectTypePath::Primitive1186/// [anonymous]: ReflectTypePath::Anonymous1187pub fn get_path(&self) -> Option<&Path> {1188match self {1189Self::Internal { custom_path, .. } => custom_path.as_ref(),1190Self::External {1191path, custom_path, ..1192} => Some(custom_path.as_ref().unwrap_or(path)),1193_ => None,1194}1195}11961197/// Returns whether this [internal] or [external] path has a custom path.1198///1199/// [internal]: ReflectTypePath::Internal1200/// [external]: ReflectTypePath::External1201pub fn has_custom_path(&self) -> bool {1202match self {1203Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {1204custom_path.is_some()1205}1206_ => false,1207}1208}12091210/// Returns a [`StringExpr`] representing the name of the type's crate.1211///1212/// Returns [`None`] if the type is [primitive] or [anonymous].1213///1214/// For non-customized [internal] paths this is created from [`module_path`].1215///1216/// For `Option<PhantomData>`, this is `"core"`.1217///1218/// [primitive]: ReflectTypePath::Primitive1219/// [anonymous]: ReflectTypePath::Anonymous1220/// [internal]: ReflectTypePath::Internal1221pub fn crate_name(&self) -> Option<StringExpr> {1222if let Some(path) = self.get_path() {1223let crate_name = &path.segments.first().unwrap().ident;1224return Some(StringExpr::from(crate_name));1225}12261227match self {1228Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {1229::core::module_path!()1230.split(':')1231.next()1232.unwrap()1233})),1234Self::External { .. } => unreachable!(),1235_ => None,1236}1237}12381239/// Combines type generics and const generics into one [`StringExpr`].1240///1241/// This string can be used with a `GenericTypePathCell` in a `TypePath` implementation.1242///1243/// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s.1244fn reduce_generics(1245generics: &Generics,1246mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,1247bevy_reflect_path: &Path,1248) -> StringExpr {1249let mut params = generics.params.iter().filter_map(|param| match param {1250GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),1251GenericParam::Const(const_param) => {1252let ident = &const_param.ident;1253let ty = &const_param.ty;12541255Some(StringExpr::Owned(quote! {1256<#ty as #bevy_reflect_path::__macro_exports::alloc_utils::ToString>::to_string(&#ident)1257}))1258}1259GenericParam::Lifetime(_) => None,1260});12611262params1263.next()1264.into_iter()1265.chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))1266.collect()1267}12681269/// Returns a [`StringExpr`] representing the "type path" of the type.1270///1271/// For `Option<PhantomData>`, this is `"std::option::Option<std::marker::PhantomData>"`.1272pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {1273match self {1274Self::Primitive(ident) => StringExpr::from(ident),1275Self::Anonymous { long_type_path, .. } => long_type_path.clone(),1276Self::Internal { generics, .. } | Self::External { generics, .. } => {1277let ident = self.type_ident().unwrap();1278let module_path = self.module_path().unwrap();12791280if self.impl_is_generic() {1281let generics = ReflectTypePath::reduce_generics(1282generics,1283|TypeParam { ident, .. }| {1284StringExpr::Borrowed(quote! {1285<#ident as #bevy_reflect_path::TypePath>::type_path()1286})1287},1288bevy_reflect_path,1289);12901291StringExpr::from_iter([1292module_path,1293StringExpr::from_str("::"),1294ident,1295StringExpr::from_str("<"),1296generics,1297StringExpr::from_str(">"),1298])1299} else {1300StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])1301}1302}1303}1304}13051306/// Returns a [`StringExpr`] representing the "short path" of the type.1307///1308/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.1309pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {1310match self {1311Self::Anonymous {1312short_type_path, ..1313} => short_type_path.clone(),1314Self::Primitive(ident) => StringExpr::from(ident),1315Self::External { generics, .. } | Self::Internal { generics, .. } => {1316let ident = self.type_ident().unwrap();13171318if self.impl_is_generic() {1319let generics = ReflectTypePath::reduce_generics(1320generics,1321|TypeParam { ident, .. }| {1322StringExpr::Borrowed(quote! {1323<#ident as #bevy_reflect_path::TypePath>::short_type_path()1324})1325},1326bevy_reflect_path,1327);13281329StringExpr::from_iter([1330ident,1331StringExpr::from_str("<"),1332generics,1333StringExpr::from_str(">"),1334])1335} else {1336ident1337}1338}1339}1340}13411342/// Returns a [`StringExpr`] representing the path to the module1343/// that the type is in.1344///1345/// Returns [`None`] if the type is [primitive] or [anonymous].1346///1347/// For non-customized [internal] paths this is created from [`module_path`].1348///1349/// For `Option<PhantomData>`, this is `"std::option"`.1350///1351/// [primitive]: ReflectTypePath::Primitive1352/// [anonymous]: ReflectTypePath::Anonymous1353/// [internal]: ReflectTypePath::Internal1354pub fn module_path(&self) -> Option<StringExpr> {1355if let Some(path) = self.get_path() {1356let path_string = path1357.segments1358.pairs()1359.take(path.segments.len() - 1)1360.map(|pair| pair.value().ident.to_string())1361.reduce(|path, ident| path + "::" + &ident)1362.unwrap();13631364let path_lit = LitStr::new(&path_string, path.span());1365return Some(StringExpr::from_lit(&path_lit));1366}13671368match self {1369Self::Internal { .. } => Some(StringExpr::Const(quote! {1370::core::module_path!()1371})),1372Self::External { .. } => unreachable!(),1373_ => None,1374}1375}13761377/// Returns a [`StringExpr`] representing the type's final ident.1378///1379/// Returns [`None`] if the type is [anonymous].1380///1381/// This is not necessarily a valid qualified path to the type.1382///1383/// For `Option<PhantomData>`, this is `"Option"`.1384///1385/// [anonymous]: ReflectTypePath::Anonymous1386pub fn type_ident(&self) -> Option<StringExpr> {1387self.get_ident().map(StringExpr::from)1388}13891390/// Returns the true type regardless of whether a custom path is specified.1391///1392/// To get the custom path if there is one, use [`Self::get_path`].1393///1394/// For example, the type `Foo<T: Debug>` would return `Foo<T>`.1395pub fn true_type(&self) -> proc_macro2::TokenStream {1396match self {1397Self::Primitive(ident) => quote!(#ident),1398Self::Internal {1399ident, generics, ..1400} => {1401let (_, ty_generics, _) = generics.split_for_impl();1402quote!(#ident #ty_generics)1403}1404Self::External { path, generics, .. } => {1405let (_, ty_generics, _) = generics.split_for_impl();1406quote!(#path #ty_generics)1407}1408Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),1409}1410}1411}14121413impl<'a> ToTokens for ReflectTypePath<'a> {1414fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {1415match self {1416Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),1417Self::External { path, .. } => path.to_tokens(tokens),1418Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),1419}1420}1421}142214231424