1- use crate :: { from_reflect, impls, ReflectDerive , REFLECT_ATTRIBUTE_NAME } ;
1+ use crate :: derive_data:: EnumVariantFields ;
2+ use crate :: {
3+ derive_data:: StructField , from_reflect, impls, utility:: ident_or_index, ReflectDerive ,
4+ REFLECT_ATTRIBUTE_NAME ,
5+ } ;
26use proc_macro:: TokenStream ;
3- use quote:: quote;
7+ use proc_macro2:: Ident ;
8+ use quote:: { format_ident, quote} ;
49use syn:: parse:: { Parse , ParseStream } ;
510use syn:: spanned:: Spanned ;
6- use syn:: { parse_macro_input, DeriveInput , ExprPath , PathArguments , Token , TypePath } ;
11+ use syn:: { parse_macro_input, DeriveInput , ExprPath , Generics , PathArguments , Token , TypePath } ;
712
813/// Generates the remote wrapper type and implements all the necessary traits.
914pub ( crate ) fn reflect_remote ( args : TokenStream , input : TokenStream ) -> TokenStream {
@@ -31,14 +36,40 @@ pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStre
3136 None
3237 } ;
3338
34- let trait_impls = match derive_data {
35- ReflectDerive :: Struct ( struct_data) | ReflectDerive :: UnitStruct ( struct_data) => {
36- proc_macro2:: TokenStream :: from ( impls:: impl_struct ( & struct_data) )
37- }
38- ReflectDerive :: TupleStruct ( struct_data) => {
39- proc_macro2:: TokenStream :: from ( impls:: impl_tuple_struct ( & struct_data) )
40- }
41- ReflectDerive :: Enum ( meta) => proc_macro2:: TokenStream :: from ( impls:: impl_enum ( & meta) ) ,
39+ let ( trait_impls, assertions) = match derive_data {
40+ ReflectDerive :: Struct ( struct_data) | ReflectDerive :: UnitStruct ( struct_data) => (
41+ proc_macro2:: TokenStream :: from ( impls:: impl_struct ( & struct_data) ) ,
42+ Some ( generate_remote_field_assertions (
43+ struct_data. fields ( ) ,
44+ None ,
45+ struct_data. meta ( ) . generics ( ) ,
46+ ) ) ,
47+ ) ,
48+ ReflectDerive :: TupleStruct ( struct_data) => (
49+ proc_macro2:: TokenStream :: from ( impls:: impl_tuple_struct ( & struct_data) ) ,
50+ Some ( generate_remote_field_assertions (
51+ struct_data. fields ( ) ,
52+ None ,
53+ struct_data. meta ( ) . generics ( ) ,
54+ ) ) ,
55+ ) ,
56+ ReflectDerive :: Enum ( enum_data) => (
57+ proc_macro2:: TokenStream :: from ( impls:: impl_enum ( & enum_data) ) ,
58+ enum_data
59+ . variants ( )
60+ . iter ( )
61+ . map ( |variant| match & variant. fields {
62+ EnumVariantFields :: Named ( fields) | EnumVariantFields :: Unnamed ( fields) => {
63+ Some ( generate_remote_field_assertions (
64+ fields,
65+ Some ( & variant. data . ident ) ,
66+ enum_data. meta ( ) . generics ( ) ,
67+ ) )
68+ }
69+ EnumVariantFields :: Unit => None ,
70+ } )
71+ . collect ( ) ,
72+ ) ,
4273 _ => {
4374 return syn:: Error :: new ( ast. span ( ) , "cannot reflect a remote value type" )
4475 . into_compile_error ( )
@@ -49,6 +80,8 @@ pub(crate) fn reflect_remote(args: TokenStream, input: TokenStream) -> TokenStre
4980 TokenStream :: from ( quote ! {
5081 #wrapper_definition
5182
83+ #assertions
84+
5285 #from_reflect_impl
5386
5487 #trait_impls
@@ -96,6 +129,74 @@ fn generate_remote_wrapper(input: &DeriveInput, remote_ty: &TypePath) -> proc_ma
96129 }
97130}
98131
132+ /// Generates compile-time assertions for remote fields.
133+ ///
134+ /// # Example
135+ ///
136+ /// The following would fail to compile due to an incorrect `#[reflect(remote = "...")]` value.
137+ ///
138+ /// ```ignore
139+ /// mod external_crate {
140+ /// pub struct TheirOuter {
141+ /// pub inner: TheirInner,
142+ /// }
143+ /// pub struct TheirInner(pub String);
144+ /// }
145+ ///
146+ /// #[reflect_remote(external_crate::TheirOuter)]
147+ /// struct MyOuter {
148+ /// #[reflect(remote = "MyOuter")] // <- Note the mismatched type (it should be `MyInner`)
149+ /// pub inner: external_crate::TheirInner,
150+ /// }
151+ ///
152+ /// #[reflect_remote(external_crate::TheirInner)]
153+ /// struct MyInner(pub String);
154+ /// ```
155+ fn generate_remote_field_assertions < ' a > (
156+ fields : & [ StructField < ' a > ] ,
157+ variant : Option < & Ident > ,
158+ generics : & Generics ,
159+ ) -> proc_macro2:: TokenStream {
160+ fields
161+ . iter ( )
162+ . filter ( |field| field. attrs . remote . is_some ( ) )
163+ . map ( |field| {
164+ let ident = if let Some ( variant) = variant {
165+ format_ident ! (
166+ "{}__{}" ,
167+ variant,
168+ ident_or_index( field. data. ident. as_ref( ) , field. index)
169+ )
170+ } else {
171+ field
172+ . data
173+ . ident
174+ . as_ref ( )
175+ . map ( |ident| ident. clone ( ) )
176+ . unwrap_or_else ( || format_ident ! ( "field_{}" , field. index) )
177+ } ;
178+
179+ let ( impl_generics, _, where_clause) = generics. split_for_impl ( ) ;
180+ let field_ty = & field. data . ty ;
181+ let remote_ty = field. attrs . remote . as_ref ( ) . unwrap ( ) ;
182+ let assertion_ident = format_ident ! ( "assert__{}__is_valid_remote" , ident) ;
183+
184+ quote ! {
185+ const _: ( ) = {
186+ struct RemoteFieldAssertions ;
187+
188+ impl RemoteFieldAssertions {
189+ #[ allow( non_snake_case) ]
190+ fn #assertion_ident #impl_generics ( #ident: #remote_ty) #where_clause {
191+ let _: #field_ty = #ident. 0 ;
192+ }
193+ }
194+ } ;
195+ }
196+ } )
197+ . collect ( )
198+ }
199+
99200/// A reflected type's remote type.
100201///
101202/// This is a wrapper around [`TypePath`] that allows it to be paired with other remote-specific logic.
0 commit comments