Path: blob/main/crates/bevy_reflect/derive/src/remote.rs
6599 views
use crate::{1derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl},2from_reflect,3ident::ident_or_index,4impls,5impls::impl_assertions,6ReflectDerive, REFLECT_ATTRIBUTE_NAME,7};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::{13parse::{Parse, ParseStream},14parse_macro_input,15spanned::Spanned,16token::PathSep,17DeriveInput, ExprPath, Generics, Member, PathArguments, Type, TypePath,18};1920/// Generates the remote wrapper type and implements all the necessary traits.21pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStream {22let remote_args = match syn::parse::<RemoteArgs>(args) {23Ok(path) => path,24Err(err) => return err.to_compile_error().into(),25};2627let remote_ty = remote_args.remote_ty;2829let ast = parse_macro_input!(input as DeriveInput);30let wrapper_definition = generate_remote_wrapper(&ast, &remote_ty);3132let mut derive_data = match ReflectDerive::from_input(33&ast,34ReflectProvenance {35source: ReflectImplSource::RemoteReflect,36trait_: ReflectTraitToImpl::Reflect,37},38) {39Ok(data) => data,40Err(err) => return err.into_compile_error().into(),41};4243derive_data.set_remote(Some(RemoteType::new(&remote_ty)));4445let assertions = impl_assertions(&derive_data);46let definition_assertions = generate_remote_definition_assertions(&derive_data);4748let reflect_remote_impl = impl_reflect_remote(&derive_data, &remote_ty);4950let (reflect_impls, from_reflect_impl) = match derive_data {51ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => (52impls::impl_struct(&struct_data),53if struct_data.meta().from_reflect().should_auto_derive() {54Some(from_reflect::impl_struct(&struct_data))55} else {56None57},58),59ReflectDerive::TupleStruct(struct_data) => (60impls::impl_tuple_struct(&struct_data),61if struct_data.meta().from_reflect().should_auto_derive() {62Some(from_reflect::impl_tuple_struct(&struct_data))63} else {64None65},66),67ReflectDerive::Enum(enum_data) => (68impls::impl_enum(&enum_data),69if enum_data.meta().from_reflect().should_auto_derive() {70Some(from_reflect::impl_enum(&enum_data))71} else {72None73},74),75ReflectDerive::Opaque(meta) => (76impls::impl_opaque(&meta),77if meta.from_reflect().should_auto_derive() {78Some(from_reflect::impl_opaque(&meta))79} else {80None81},82),83};8485TokenStream::from(quote! {86#wrapper_definition8788const _: () = {89#reflect_remote_impl9091#reflect_impls9293#from_reflect_impl9495#definition_assertions9697#assertions98};99})100}101102/// Generates the remote wrapper type.103///104/// # Example105///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 {115let ident = &input.ident;116let vis = &input.vis;117let ty_generics = &input.generics;118let where_clause = &input.generics.where_clause;119let attrs = input120.attrs121.iter()122.filter(|attr| !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME));123124quote! {125#(#attrs)*126#[repr(transparent)]127#[doc(hidden)]128#vis struct #ident #ty_generics (pub #remote_ty) #where_clause;129}130}131132/// Generates the implementation of the `ReflectRemote` trait for the given derive data and remote type.133///134/// # Note to Developers135///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 {140let bevy_reflect_path = input.meta().bevy_reflect_path();141142let type_path = input.meta().type_path();143let (impl_generics, ty_generics, where_clause) =144input.meta().type_path().generics().split_for_impl();145146let where_reflect_clause = input147.where_clause_options()148.extend_where_clause(where_clause);149150quote! {151// SAFE: The generated wrapper type is guaranteed to be valid and repr(transparent) over the remote type.152impl #impl_generics #bevy_reflect_path::ReflectRemote for #type_path #ty_generics #where_reflect_clause {153type Remote = #remote_ty;154155fn as_remote(&self) -> &Self::Remote {156&self.0157}158fn as_remote_mut(&mut self) -> &mut Self::Remote {159&mut self.0160}161fn into_remote(self) -> Self::Remote162{163// SAFE: The wrapper type should be repr(transparent) over the remote type164unsafe {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 types168// |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}181182fn as_wrapper(remote: &Self::Remote) -> &Self {183// SAFE: The wrapper type should be repr(transparent) over the remote type184unsafe { ::core::mem::transmute::<&Self::Remote, &Self>(remote) }185}186fn as_wrapper_mut(remote: &mut Self::Remote) -> &mut Self {187// SAFE: The wrapper type should be repr(transparent) over the remote type188unsafe { ::core::mem::transmute::<&mut Self::Remote, &mut Self>(remote) }189}190fn into_wrapper(remote: Self::Remote) -> Self191{192// SAFE: The wrapper type should be repr(transparent) over the remote type193unsafe {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 types197// |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}213214/// 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 that218/// 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/// # Example222///223/// The following would fail to compile due to an incorrect `#[reflect(remote = ...)]` value.224///225/// ```ignore226/// 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::TheirFoo240/// }241/// ```242pub(crate) fn generate_remote_assertions(243derive_data: &ReflectDerive,244) -> Option<proc_macro2::TokenStream> {245struct RemoteAssertionData<'a> {246ident: Member,247variant: Option<&'a Ident>,248ty: &'a Type,249generics: &'a Generics,250remote_ty: &'a Type,251}252253let bevy_reflect_path = derive_data.meta().bevy_reflect_path();254255let fields: Box<dyn Iterator<Item = RemoteAssertionData>> = match derive_data {256ReflectDerive::Struct(data)257| ReflectDerive::TupleStruct(data)258| ReflectDerive::UnitStruct(data) => Box::new(data.active_fields().filter_map(|field| {259field260.attrs261.remote262.as_ref()263.map(|remote_ty| RemoteAssertionData {264ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),265variant: None,266ty: &field.data.ty,267generics: data.meta().type_path().generics(),268remote_ty,269})270})),271ReflectDerive::Enum(data) => Box::new(data.variants().iter().flat_map(|variant| {272variant.active_fields().filter_map(|field| {273field274.attrs275.remote276.as_ref()277.map(|remote_ty| RemoteAssertionData {278ident: ident_or_index(field.data.ident.as_ref(), field.declaration_index),279variant: Some(&variant.data.ident),280ty: &field.data.ty,281generics: data.meta().type_path().generics(),282remote_ty,283})284})285})),286287_ => return None,288};289290let assertions = fields291.map(move |field| {292let ident = if let Some(variant) = field.variant {293format_ident!("{}__{}", variant, field.ident)294} else {295match field.ident {296Member::Named(ident) => ident,297Member::Unnamed(index) => format_ident!("field_{}", index),298}299};300let (impl_generics, _, where_clause) = field.generics.split_for_impl();301302let where_reflect_clause = derive_data303.where_clause_options()304.extend_where_clause(where_clause);305306let ty = &field.ty;307let remote_ty = field.remote_ty;308let assertion_ident = format_ident!("assert__{}__is_valid_remote", ident);309310let span = create_assertion_span(remote_ty.span());311312quote_spanned! {span=>313#[allow(non_snake_case)]314#[allow(clippy::multiple_bound_locations)]315fn #assertion_ident #impl_generics () #where_reflect_clause {316let _: <#remote_ty as #bevy_reflect_path::ReflectRemote>::Remote = (|| -> #FQOption<#ty> {317None318})().unwrap();319}320}321})322.collect::<proc_macro2::TokenStream>();323324if assertions.is_empty() {325None326} else {327Some(quote! {328struct RemoteFieldAssertions;329330impl RemoteFieldAssertions {331#assertions332}333})334}335}336337/// Generates compile-time assertions that ensure a remote wrapper definition matches up with the338/// remote type it's wrapping.339///340/// Note: This currently results in "backwards" error messages like:341///342/// ```ignore343/// 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 without348/// 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 {350let meta = derive_data.meta();351let self_ident = format_ident!("__remote__");352let self_ty = derive_data.remote_ty().unwrap().type_path();353let self_expr_path = derive_data.remote_ty().unwrap().as_expr_path().unwrap();354let (impl_generics, _, where_clause) = meta.type_path().generics().split_for_impl();355356let where_reflect_clause = derive_data357.where_clause_options()358.extend_where_clause(where_clause);359360let assertions = match derive_data {361ReflectDerive::Struct(data)362| ReflectDerive::TupleStruct(data)363| ReflectDerive::UnitStruct(data) => {364let mut output = proc_macro2::TokenStream::new();365366for field in data.fields() {367let field_member =368ident_or_index(field.data.ident.as_ref(), field.declaration_index);369let field_ty = &field.data.ty;370let span = create_assertion_span(field_ty.span());371372output.extend(quote_spanned! {span=>373#self_ident.#field_member = (|| -> #FQOption<#field_ty> {None})().unwrap();374});375}376377output378}379ReflectDerive::Enum(data) => {380let variants = data.variants().iter().map(|variant| {381let ident = &variant.data.ident;382383let mut output = proc_macro2::TokenStream::new();384385if variant.fields().is_empty() {386return quote!(#self_expr_path::#ident => {});387}388389for field in variant.fields() {390let field_member =391ident_or_index(field.data.ident.as_ref(), field.declaration_index);392let field_ident = format_ident!("field_{}", field_member);393let field_ty = &field.data.ty;394let span = create_assertion_span(field_ty.span());395396output.extend(quote_spanned! {span=>397#self_expr_path::#ident {#field_member: mut #field_ident, ..} => {398#field_ident = (|| -> #FQOption<#field_ty> {None})().unwrap();399}400});401}402403output404});405406quote! {407match #self_ident {408#(#variants)*409}410}411}412ReflectDerive::Opaque(_) => {413// No assertions needed since there are no fields to check414proc_macro2::TokenStream::new()415}416};417418quote! {419const _: () = {420#[allow(non_snake_case)]421#[allow(unused_variables)]422#[allow(unused_assignments)]423#[allow(unreachable_patterns)]424#[allow(clippy::multiple_bound_locations)]425fn assert_wrapper_definition_matches_remote_type #impl_generics (mut #self_ident: #self_ty) #where_reflect_clause {426#assertions427}428};429}430}431432/// Creates a span located around the given one, but resolves to the assertion's context.433///434/// This should allow the compiler to point back to the line and column in the user's code,435/// while still attributing the error to the macro.436fn create_assertion_span(span: Span) -> Span {437Span::call_site().located_at(span)438}439440/// A reflected type's remote type.441///442/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.443#[derive(Copy, Clone)]444pub(crate) struct RemoteType<'a> {445path: &'a TypePath,446}447448impl<'a> RemoteType<'a> {449pub fn new(path: &'a TypePath) -> Self {450Self { path }451}452453/// Returns the [type path](TypePath) of this remote type.454pub fn type_path(&self) -> &'a TypePath {455self.path456}457458/// Attempts to convert the [type path](TypePath) of this remote type into an [expression path](ExprPath).459///460/// For example, this would convert `foo::Bar<T>` into `foo::Bar::<T>` to be used as part of an expression.461///462/// This will return an error for types that are parenthesized, such as in `Fn() -> Foo`.463pub fn as_expr_path(&self) -> Result<ExprPath, syn::Error> {464let mut expr_path = self.path.clone();465if let Some(segment) = expr_path.path.segments.last_mut() {466match &mut segment.arguments {467PathArguments::None => {}468PathArguments::AngleBracketed(arg) => {469arg.colon2_token = Some(PathSep::default());470}471PathArguments::Parenthesized(arg) => {472return Err(syn::Error::new(473arg.span(),474"cannot use parenthesized type as remote type",475))476}477}478}479480Ok(ExprPath {481path: expr_path.path,482qself: expr_path.qself,483attrs: Vec::new(),484})485}486}487488/// Metadata from the arguments defined in the `reflect_remote` attribute.489///490/// The syntax for the arguments is: `#[reflect_remote(REMOTE_TYPE_PATH)]`.491struct RemoteArgs {492remote_ty: TypePath,493}494495impl Parse for RemoteArgs {496fn parse(input: ParseStream) -> syn::Result<Self> {497Ok(Self {498remote_ty: input.parse()?,499})500}501}502503504