bevy_reflect_derive/
remote.rs

1use crate::{
2    derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},
3    from_reflect, impls,
4    impls::impl_assertions,
5    ReflectDerive, REFLECT_ATTRIBUTE_NAME,
6};
7use bevy_macro_utils::as_member;
8use bevy_macro_utils::fq_std::FQOption;
9use proc_macro::TokenStream;
10use proc_macro2::{Ident, Span};
11use quote::{format_ident, quote, quote_spanned};
12use syn::{
13    parse::{Parse, ParseStream},
14    parse_macro_input,
15    spanned::Spanned,
16    token::PathSep,
17    DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,
18};
19
20/// Generates the remote wrapper type and implements all the necessary traits.
21pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {
22    let remote_args = match syn::parse::<RemoteArgs>(args) {
23        Ok(path) => path,
24        Err(err) => return err.to_compile_error().into(),
25    };
26
27    let remote_ty = remote_args.remote_ty;
28
29    let ast = parse_macro_input!(input as DeriveInput);
30    let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);
31
32    let mut derive_data = match ReflectDerive::from_input(
33        &ast,
34        ReflectProvenance {
35            source: ReflectImplSource::RemoteReflect,
36            trait_: ReflectTraitToImpl::Reflect,
37        },
38    ) {
39        Ok(data) => data,
40        Err(err) => return err.into_compile_error().into(),
41    };
42
43    derive_data.set_remote(Some(RemoteType::new(&remote_ty)));
44
45    let assertions = impl_assertions(&derive_data);
46    let definition_assertions = generate_remote_definition_assertions(&derive_data);
47
48    let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);
49
50    let (reflect_impls, from_reflect_impl) = match derive_data {
51        ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (
52            impls::impl_struct(&struct_data),
53            if struct_data.meta().from_reflect().should_auto_derive() {
54                Some(from_reflect::impl_struct(&struct_data))
55            } else {
56                None
57            },
58        ),
59        ReflectDerive::TupleStruct(struct_data) => (
60            impls::impl_tuple_struct(&struct_data),
61            if struct_data.meta().from_reflect().should_auto_derive() {
62                Some(from_reflect::impl_tuple_struct(&struct_data))
63            } else {
64                None
65            },
66        ),
67        ReflectDerive::Enum(enum_data) => (
68            impls::impl_enum(&enum_data),
69            if enum_data.meta().from_reflect().should_auto_derive() {
70                Some(from_reflect::impl_enum(&enum_data))
71            } else {
72                None
73            },
74        ),
75        ReflectDerive::Opaque(meta) => (
76            impls::impl_opaque(&meta),
77            if meta.from_reflect().should_auto_derive() {
78                Some(from_reflect::impl_opaque(&meta))
79            } else {
80                None
81            },
82        ),
83    };
84
85    TokenStream::from(quote! {
86        #wrapper_definition
87
88        const _: () = {
89            #reflect_remote_impl
90
91            #reflect_impls
92
93            #from_reflect_impl
94
95            #definition_assertions
96
97            #assertions
98        };
99    })
100}
101
102/// Generates the remote wrapper type.
103///
104/// # Example
105///
106/// If the supplied remote type is `Bar<T>`, then the wrapper type— named `Foo<T>`— would look like:
107///
108/// ```
109/// # struct Bar<T>(T);
110///
111/// #[repr(transparent)]
112/// struct Foo<T>(Bar<T>);
113/// ```
114fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_macro2::TokenStream {
115    let ident = &input.ident;
116    let vis = &input.vis;
117    let ty_generics = &input.generics;
118    let where_clause = &input.generics.where_clause;
119    let attrs = input
120        .attrs
121        .iter()
122        .filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));
123
124    quote! {
125        #(#attrs)*
126        #[repr(transparent)]
127        #[doc(hidden)]
128        #vis struct #ident #ty_generics (pub #remote_ty) #where_clause;
129    }
130}
131
132/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.
133///
134/// # Note to Developers
135///
136/// The `ReflectRemote` trait could likely be made with default method implementations.
137/// However, this makes it really easy for a user to accidentally implement this trait in an unsafe way.
138/// To prevent this, we instead generate the implementation through a macro using this function.
139fn impl_reflect_remote(input: &ReflectDerive, remote_ty: &TypePath) -> proc_macro2::TokenStream {
140    let bevy_reflect_path = input.meta().bevy_reflect_path();
141
142    let type_path = input.meta().type_path();
143    let (impl_generics, ty_generics, where_clause) =
144        input.meta().type_path().generics().split_for_impl();
145
146    let where_reflect_clause = input
147        .where_clause_options()
148        .extend_where_clause(where_clause);
149
150    quote! {
151        // SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.
152        impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {
153            type Remote = #remote_ty;
154
155            fn as_remote(&self) -> &Self::Remote {
156                &self.0
157            }
158            fn as_remote_mut(&mut self) -> &mut Self::Remote {
159                &mut self.0
160            }
161            fn into_remote(self) -> Self::Remote
162            {
163                // SAFE: The wrapper type should be repr(transparent) over the remote type
164                unsafe {
165                    // Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
166                    // ```
167                    // error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
168                    // |
169                    // |                 core::mem::transmute::<A, B>(a)
170                    // |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
171                    // |
172                    // = note: source type: `A` (this type does not have a fixed size)
173                    // = note: target type: `B` (this type does not have a fixed size)
174                    // ```
175                    ::core::mem::transmute_copy::<Self, Self::Remote>(
176                        // `ManuallyDrop` is used to prevent double-dropping `self`
177                        &::core::mem::ManuallyDrop::new(self)
178                    )
179                }
180            }
181
182            fn as_wrapper(remote: &Self::Remote) -> &Self {
183                // SAFE: The wrapper type should be repr(transparent) over the remote type
184                unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }
185            }
186            fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {
187                // SAFE: The wrapper type should be repr(transparent) over the remote type
188                unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }
189            }
190            fn into_wrapper(remote: Self::Remote) -> Self
191            {
192                // SAFE: The wrapper type should be repr(transparent) over the remote type
193                unsafe {
194                    // Unfortunately, we have to use `transmute_copy` to avoid a compiler error:
195                    // ```
196                    // error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
197                    // |
198                    // |                 core::mem::transmute::<A, B>(a)
199                    // |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
200                    // |
201                    // = note: source type: `A` (this type does not have a fixed size)
202                    // = note: target type: `B` (this type does not have a fixed size)
203                    // ```
204                    ::core::mem::transmute_copy::<Self::Remote, Self>(
205                        // `ManuallyDrop` is used to prevent double-dropping `self`
206                        &::core::mem::ManuallyDrop::new(remote)
207                    )
208                }
209            }
210        }
211    }
212}
213
214/// Generates compile-time assertions for remote fields.
215///
216/// This prevents types from using an invalid remote type.
217/// this works by generating a struct, `RemoteFieldAssertions`, with methods that
218/// will result in compile-time failure if types are mismatched.
219/// The output of this function is best placed within an anonymous context to maintain hygiene.
220///
221/// # Example
222///
223/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.
224///
225/// ```ignore
226/// mod external_crate {
227///     pub struct TheirFoo(pub u32);
228///     pub struct TheirBar(pub i32);
229/// }
230///
231/// #[reflect_remote(external_crate::TheirFoo)]
232/// struct MyFoo(pub u32);
233/// #[reflect_remote(external_crate::TheirBar)]
234/// struct MyBar(pub i32);
235///
236/// #[derive(Reflect)]
237/// struct MyStruct {
238///   #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar`
239///   foo: external_crate::TheirFoo
240/// }
241/// ```
242pub(crate) fn generate_remote_assertions(
243    derive_data: &ReflectDerive,
244) -> Option<proc_macro2::TokenStream> {
245    struct RemoteAssertionData<'a> {
246        ident: Member,
247        variant: Option<&'a Ident>,
248        ty: &'a Type,
249        generics: &'a Generics,
250        remote_ty: &'a Type,
251    }
252
253    let bevy_reflect_path = derive_data.meta().bevy_reflect_path();
254
255    let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {
256        ReflectDerive::Struct(data)
257        | ReflectDerive::TupleStruct(data)
258        | ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {
259            field
260                .attrs
261                .remote
262                .as_ref()
263                .map(|remote_ty| RemoteAssertionData {
264                    ident: as_member(field.data.ident.as_ref(), field.declaration_index),
265                    variant: None,
266                    ty: &field.data.ty,
267                    generics: data.meta().type_path().generics(),
268                    remote_ty,
269                })
270        })),
271        ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {
272            variant.active_fields().filter_map(|field| {
273                field
274                    .attrs
275                    .remote
276                    .as_ref()
277                    .map(|remote_ty| RemoteAssertionData {
278                        ident: as_member(field.data.ident.as_ref(), field.declaration_index),
279                        variant: Some(&variant.data.ident),
280                        ty: &field.data.ty,
281                        generics: data.meta().type_path().generics(),
282                        remote_ty,
283                    })
284            })
285        })),
286
287        _ => return None,
288    };
289
290    let assertions = fields
291        .map(move |field| {
292            let ident = if let Some(variant) = field.variant {
293                format_ident!("{}__{}", variant, field.ident)
294            } else {
295                match field.ident {
296                    Member::Named(ident) => ident,
297                    Member::Unnamed(index) => format_ident!("field_{}", index),
298                }
299            };
300            let (impl_generics, _, where_clause) = field.generics.split_for_impl();
301
302            let where_reflect_clause = derive_data
303                .where_clause_options()
304                .extend_where_clause(where_clause);
305
306            let ty = &field.ty;
307            let remote_ty = field.remote_ty;
308            let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);
309
310            let span = create_assertion_span(remote_ty.span());
311
312            quote_spanned! {span=>
313                #[allow(non_snake_case)]
314                #[allow(clippy::multiple_bound_locations)]
315                fn #assertion_ident #impl_generics () #where_reflect_clause {
316                    let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {
317                        None
318                    })().unwrap();
319                }
320            }
321        })
322        .collect::<proc_macro2::TokenStream>();
323
324    if assertions.is_empty() {
325        None
326    } else {
327        Some(quote! {
328            struct RemoteFieldAssertions;
329
330            impl RemoteFieldAssertions {
331                #assertions
332            }
333        })
334    }
335}
336
337/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the
338/// remote type it's wrapping.
339///
340/// Note: This currently results in "backwards" error messages like:
341///
342/// ```ignore
343/// expected: <WRAPPER_FIELD_TYPE>
344/// found: <REMOTE_FIELD_TYPE>
345/// ```
346///
347/// Ideally it would be the other way around, but there's no easy way of doing this without
348/// generating a copy of the struct/enum definition and using that as the base instead of the remote type.
349fn generate_remote_definition_assertions(derive_data: &ReflectDerive) -> proc_macro2::TokenStream {
350    let meta = derive_data.meta();
351    let self_ident = format_ident!("__remote__");
352    let self_ty = derive_data.remote_ty().unwrap().type_path();
353    let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();
354    let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();
355
356    let where_reflect_clause = derive_data
357        .where_clause_options()
358        .extend_where_clause(where_clause);
359
360    let assertions = match derive_data {
361        ReflectDerive::Struct(data)
362        | ReflectDerive::TupleStruct(data)
363        | ReflectDerive::UnitStruct(data) => {
364            let mut output = proc_macro2::TokenStream::new();
365
366            for field in data.fields() {
367                let field_member = as_member(field.data.ident.as_ref(), field.declaration_index);
368                let field_ty = &field.data.ty;
369                let span = create_assertion_span(field_ty.span());
370
371                output.extend(quote_spanned! {span=>
372                    #self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();
373                });
374            }
375
376            output
377        }
378        ReflectDerive::Enum(data) => {
379            let variants = data.variants().iter().map(|variant| {
380                let ident = &variant.data.ident;
381
382                let mut output = proc_macro2::TokenStream::new();
383
384                if variant.fields().is_empty() {
385                    return quote!(#self_expr_path::#ident => {});
386                }
387
388                for field in variant.fields() {
389                    let field_member =
390                        as_member(field.data.ident.as_ref(), field.declaration_index);
391                    let field_ident = format_ident!("field_{}", field_member);
392                    let field_ty = &field.data.ty;
393                    let span = create_assertion_span(field_ty.span());
394
395                    output.extend(quote_spanned! {span=>
396                        #self_expr_path::#ident {#field_member: mut #field_ident, ..} => {
397                            #field_ident =  (|| -> #FQOption<#field_ty> {None})().unwrap();
398                        }
399                    });
400                }
401
402                output
403            });
404
405            quote! {
406                match #self_ident {
407                    #(#variants)*
408                }
409            }
410        }
411        ReflectDerive::Opaque(_) => {
412            // No assertions needed since there are no fields to check
413            proc_macro2::TokenStream::new()
414        }
415    };
416
417    quote! {
418        const _: () = {
419            #[allow(non_snake_case)]
420            #[allow(unused_variables)]
421            #[allow(unused_assignments)]
422            #[allow(unreachable_patterns)]
423            #[allow(clippy::multiple_bound_locations)]
424            fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {
425                #assertions
426            }
427        };
428    }
429}
430
431/// Creates a span located around the given one, but resolves to the assertion's context.
432///
433/// This should allow the compiler to point back to the line and column in the user's code,
434/// while still attributing the error to the macro.
435fn create_assertion_span(span: Span) -> Span {
436    Span::call_site().located_at(span)
437}
438
439/// A reflected type's remote type.
440///
441/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
442#[derive(Copy, Clone)]
443pub(crate) struct RemoteType<'a> {
444    path: &'a TypePath,
445}
446
447impl<'a> RemoteType<'a> {
448    pub fn new(path: &'a TypePath) -> Self {
449        Self { path }
450    }
451
452    /// Returns the [type path](TypePath) of this remote type.
453    pub fn type_path(&self) -> &'a TypePath {
454        self.path
455    }
456
457    /// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).
458    ///
459    /// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.
460    ///
461    /// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.
462    pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {
463        let mut expr_path = self.path.clone();
464        if let Some(segment) = expr_path.path.segments.last_mut() {
465            match &mut segment.arguments {
466                PathArguments::None => {}
467                PathArguments::AngleBracketed(arg) => {
468                    arg.colon2_token = Some(PathSep::default());
469                }
470                PathArguments::Parenthesized(arg) => {
471                    return Err(syn::Error::new(
472                        arg.span(),
473                        "cannot use parenthesized type as remote type",
474                    ))
475                }
476            }
477        }
478
479        Ok(ExprPath {
480            path: expr_path.path,
481            qself: expr_path.qself,
482            attrs: Vec::new(),
483        })
484    }
485}
486
487/// Metadata from the arguments defined in the `reflect_remote` attribute.
488///
489/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.
490struct RemoteArgs {
491    remote_ty: TypePath,
492}
493
494impl Parse for RemoteArgs {
495    fn parse(input: ParseStream) -> syn::Result<Self> {
496        Ok(Self {
497            remote_ty: input.parse()?,
498        })
499    }
500}