Path: blob/main/crates/bevy_reflect/derive/src/remote.rs
9418 views
use crate::{1derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},2from_reflect, impls,3impls::impl_assertions,4ReflectDerive, REFLECT_ATTRIBUTE_NAME,5};6use bevy_macro_utils::as_member;7use bevy_macro_utils::fq_std::FQOption;8use proc_macro::TokenStream;9use proc_macro2::{Ident, Span};10use quote::{format_ident, quote, quote_spanned};11use syn::{12parse::{Parse, ParseStream},13parse_macro_input,14spanned::Spanned,15token::PathSep,16DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,17};1819/// Generates the remote wrapper type and implements all the necessary traits.20pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {21let remote_args = match syn::parse::<RemoteArgs>(args) {22Ok(path) => path,23Err(err) => return err.to_compile_error().into(),24};2526let remote_ty = remote_args.remote_ty;2728let ast = parse_macro_input!(input as DeriveInput);29let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);3031let mut derive_data = match ReflectDerive::from_input(32&ast,33ReflectProvenance {34source: ReflectImplSource::RemoteReflect,35trait_: ReflectTraitToImpl::Reflect,36},37) {38Ok(data) => data,39Err(err) => return err.into_compile_error().into(),40};4142derive_data.set_remote(Some(RemoteType::new(&remote_ty)));4344let assertions = impl_assertions(&derive_data);45let definition_assertions = generate_remote_definition_assertions(&derive_data);4647let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);4849let (reflect_impls, from_reflect_impl) = match derive_data {50ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (51impls::impl_struct(&struct_data),52if struct_data.meta().from_reflect().should_auto_derive() {53Some(from_reflect::impl_struct(&struct_data))54} else {55None56},57),58ReflectDerive::TupleStruct(struct_data) => (59impls::impl_tuple_struct(&struct_data),60if struct_data.meta().from_reflect().should_auto_derive() {61Some(from_reflect::impl_tuple_struct(&struct_data))62} else {63None64},65),66ReflectDerive::Enum(enum_data) => (67impls::impl_enum(&enum_data),68if enum_data.meta().from_reflect().should_auto_derive() {69Some(from_reflect::impl_enum(&enum_data))70} else {71None72},73),74ReflectDerive::Opaque(meta) => (75impls::impl_opaque(&meta),76if meta.from_reflect().should_auto_derive() {77Some(from_reflect::impl_opaque(&meta))78} else {79None80},81),82};8384TokenStream::from(quote! {85#wrapper_definition8687const _: () = {88#reflect_remote_impl8990#reflect_impls9192#from_reflect_impl9394#definition_assertions9596#assertions97};98})99}100101/// Generates the remote wrapper type.102///103/// # Example104///105/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:106///107/// ```108/// # struct Bar<T>(T);109///110/// #[repr(transparent)]111/// struct Foo<T>(Bar<T>);112/// ```113fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {114let ident = &input.ident;115let vis = &input.vis;116let ty_generics = &input.generics;117let where_clause = &input.generics.where_clause;118let attrs = input119.attrs120.iter()121.filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));122123quote! {124#(#attrs)*125#[repr(transparent)]126#[doc(hidden)]127#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;128}129}130131/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.132///133/// # Note to Developers134///135/// The `ReflectRemote` trait could likely be made with default method implementations.136/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.137/// To prevent this, we instead generate the implementation through a macro using this function.138fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {139let bevy_reflect_path = input.meta().bevy_reflect_path();140141let type_path = input.meta().type_path();142let (impl_generics, ty_generics, where_clause) =143input.meta().type_path().generics().split_for_impl();144145let where_reflect_clause = input146.where_clause_options()147.extend_where_clause(where_clause);148149quote! {150// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.151impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {152type Remote = #remote_ty;153154fn as_remote(&self) -> &Self::Remote {155&self.0156}157fn as_remote_mut(&mut self) -> &mut Self::Remote {158&mut self.0159}160fn into_remote(self) -> Self::Remote161{162// SAFE: The wrapper type should be repr(transparent) over the remote type163unsafe {164// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:165// ```166// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types167// |168// | core::mem::transmute::<A, B>(a)169// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^170// |171// = note: source type: `A` (this type does not have a fixed size)172// = note: target type: `B` (this type does not have a fixed size)173// ```174::core::mem::transmute_copy::<Self, Self::Remote>(175// `ManuallyDrop` is used to prevent double-dropping `self`176&::core::mem::ManuallyDrop::new(self)177)178}179}180181fn as_wrapper(remote: &Self::Remote) -> &Self {182// SAFE: The wrapper type should be repr(transparent) over the remote type183unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }184}185fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {186// SAFE: The wrapper type should be repr(transparent) over the remote type187unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }188}189fn into_wrapper(remote: Self::Remote) -> Self190{191// SAFE: The wrapper type should be repr(transparent) over the remote type192unsafe {193// Unfortunately, we have to use `transmute_copy` to avoid a compiler error:194// ```195// error[E0512]: cannot transmute between types of different sizes, or dependently-sized types196// |197// | core::mem::transmute::<A, B>(a)198// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^199// |200// = note: source type: `A` (this type does not have a fixed size)201// = note: target type: `B` (this type does not have a fixed size)202// ```203::core::mem::transmute_copy::<Self::Remote, Self>(204// `ManuallyDrop` is used to prevent double-dropping `self`205&::core::mem::ManuallyDrop::new(remote)206)207}208}209}210}211}212213/// Generates compile-time assertions for remote fields.214///215/// This prevents types from using an invalid remote type.216/// this works by generating a struct, `RemoteFieldAssertions`, with methods that217/// will result in compile-time failure if types are mismatched.218/// The output of this function is best placed within an anonymous context to maintain hygiene.219///220/// # Example221///222/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.223///224/// ```ignore225/// mod external_crate {226/// pub struct TheirFoo(pub u32);227/// pub struct TheirBar(pub i32);228/// }229///230/// #[reflect_remote(external_crate::TheirFoo)]231/// struct MyFoo(pub u32);232/// #[reflect_remote(external_crate::TheirBar)]233/// struct MyBar(pub i32);234///235/// #[derive(Reflect)]236/// struct MyStruct {237/// #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`238/// foo: external_crate::TheirFoo239/// }240/// ```241pub(crate) fn generate_remote_assertions(242derive_data: &ReflectDerive,243) -> Option<proc_macro2::TokenStream> {244struct RemoteAssertionData<'a> {245ident: Member,246variant: Option<&'a Ident>,247ty: &'a Type,248generics: &'a Generics,249remote_ty: &'a Type,250}251252let bevy_reflect_path = derive_data.meta().bevy_reflect_path();253254let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {255ReflectDerive::Struct(data)256| ReflectDerive::TupleStruct(data)257| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {258field259.attrs260.remote261.as_ref()262.map(|remote_ty| RemoteAssertionData {263ident: as_member(field.data.ident.as_ref(), field.declaration_index),264variant: None,265ty: &field.data.ty,266generics: data.meta().type_path().generics(),267remote_ty,268})269})),270ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {271variant.active_fields().filter_map(|field| {272field273.attrs274.remote275.as_ref()276.map(|remote_ty| RemoteAssertionData {277ident: as_member(field.data.ident.as_ref(), field.declaration_index),278variant: Some(&variant.data.ident),279ty: &field.data.ty,280generics: data.meta().type_path().generics(),281remote_ty,282})283})284})),285286_ => return None,287};288289let assertions = fields290.map(move |field| {291let ident = if let Some(variant) = field.variant {292format_ident!("{}__{}", variant, field.ident)293} else {294match field.ident {295Member::Named(ident) => ident,296Member::Unnamed(index) => format_ident!("field_{}", index),297}298};299let (impl_generics, _, where_clause) = field.generics.split_for_impl();300301let where_reflect_clause = derive_data302.where_clause_options()303.extend_where_clause(where_clause);304305let ty = &field.ty;306let remote_ty = field.remote_ty;307let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);308309let span = create_assertion_span(remote_ty.span());310311quote_spanned! {span=>312#[allow(non_snake_case)]313#[allow(clippy::multiple_bound_locations)]314fn #assertion_ident #impl_generics () #where_reflect_clause {315let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {316None317})().unwrap();318}319}320})321.collect::<proc_macro2::TokenStream>();322323if assertions.is_empty() {324None325} else {326Some(quote! {327struct RemoteFieldAssertions;328329impl RemoteFieldAssertions {330#assertions331}332})333}334}335336/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the337/// remote type it's wrapping.338///339/// Note: This currently results in "backwards" error messages like:340///341/// ```ignore342/// expected: <WRAPPER_FIELD_TYPE>343/// found: <REMOTE_FIELD_TYPE>344/// ```345///346/// Ideally it would be the other way around, but there's no easy way of doing this without347/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.348fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {349let meta = derive_data.meta();350let self_ident = format_ident!("__remote__");351let self_ty = derive_data.remote_ty().unwrap().type_path();352let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();353let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();354355let where_reflect_clause = derive_data356.where_clause_options()357.extend_where_clause(where_clause);358359let assertions = match derive_data {360ReflectDerive::Struct(data)361| ReflectDerive::TupleStruct(data)362| ReflectDerive::UnitStruct(data) => {363let mut output = proc_macro2::TokenStream::new();364365for field in data.fields() {366let field_member = as_member(field.data.ident.as_ref(), field.declaration_index);367let field_ty = &field.data.ty;368let span = create_assertion_span(field_ty.span());369370output.extend(quote_spanned! {span=>371#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();372});373}374375output376}377ReflectDerive::Enum(data) => {378let variants = data.variants().iter().map(|variant| {379let ident = &variant.data.ident;380381let mut output = proc_macro2::TokenStream::new();382383if variant.fields().is_empty() {384return quote!(#self_expr_path::#ident => {});385}386387for field in variant.fields() {388let field_member =389as_member(field.data.ident.as_ref(), field.declaration_index);390let field_ident = format_ident!("field_{}", field_member);391let field_ty = &field.data.ty;392let span = create_assertion_span(field_ty.span());393394output.extend(quote_spanned! {span=>395#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {396#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();397}398});399}400401output402});403404quote! {405match #self_ident {406#(#variants)*407}408}409}410ReflectDerive::Opaque(_) => {411// No assertions needed since there are no fields to check412proc_macro2::TokenStream::new()413}414};415416quote! {417const _: () = {418#[allow(non_snake_case)]419#[allow(unused_variables)]420#[allow(unused_assignments)]421#[allow(unreachable_patterns)]422#[allow(clippy::multiple_bound_locations)]423fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {424#assertions425}426};427}428}429430/// Creates a span located around the given one, but resolves to the assertion's context.431///432/// This should allow the compiler to point back to the line and column in the user's code,433/// while still attributing the error to the macro.434fn create_assertion_span(span: Span) -> Span {435Span::call_site().located_at(span)436}437438/// A reflected type's remote type.439///440/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.441#[derive(Copy, Clone)]442pub(crate) struct RemoteType<'a> {443path: &'a TypePath,444}445446impl<'a> RemoteType<'a> {447pub fn new(path: &'a TypePath) -> Self {448Self { path }449}450451/// Returns the [type path](TypePath) of this remote type.452pub fn type_path(&self) -> &'a TypePath {453self.path454}455456/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).457///458/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.459///460/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.461pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {462let mut expr_path = self.path.clone();463if let Some(segment) = expr_path.path.segments.last_mut() {464match &mut segment.arguments {465PathArguments::None => {}466PathArguments::AngleBracketed(arg) => {467arg.colon2_token = Some(PathSep::default());468}469PathArguments::Parenthesized(arg) => {470return Err(syn::Error::new(471arg.span(),472"cannot use parenthesized type as remote type",473))474}475}476}477478Ok(ExprPath {479path: expr_path.path,480qself: expr_path.qself,481attrs: Vec::new(),482})483}484}485486/// Metadata from the arguments defined in the `reflect_remote` attribute.487///488/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.489struct RemoteArgs {490remote_ty: TypePath,491}492493impl Parse for RemoteArgs {494fn parse(input: ParseStream) -> syn::Result<Self> {495Ok(Self {496remote_ty: input.parse()?,497})498}499}500501502