11#![ recursion_limit = "1024" ]
22
33extern crate proc_macro;
4+
45use proc_macro2:: Span ;
5- use quote:: { ToTokens , quote} ;
6+ use quote:: { ToTokens , format_ident, quote} ;
7+ use syn:: Ident ;
8+
9+ fn extract_idents_and_offset (
10+ fields : & syn:: punctuated:: Punctuated < syn:: Field , syn:: token:: Comma > ,
11+ ) -> ( Vec < ( proc_macro2:: TokenStream , & syn:: Field ) > , syn:: Ident ) {
12+ // first iterate idents
13+ let idents: Vec < _ > = fields
14+ . into_iter ( )
15+ . enumerate ( )
16+ . map ( |( i, f) | {
17+ let ident = f. ident . as_ref ( ) . map ( |i| quote ! { #i} ) . unwrap_or ( {
18+ let t = proc_macro2:: Literal :: usize_unsuffixed ( i) ;
19+ quote ! { #t}
20+ } ) ;
21+ ( ident, f)
22+ } )
23+ . collect ( ) ;
24+ // iterate until we have no field that matches our offset
25+ let offset = fresh_name (
26+ & fields,
27+ proc_macro2:: Ident :: new ( "offset" , Span :: call_site ( ) ) ,
28+ ) ;
29+
30+ ( idents, offset)
31+ }
632
7- use proc_macro:: TokenStream ;
33+ /// Generates a fresh name that will not clash with any field named the same
34+ /// NB: there is probably a more efficient algorithm than this worst case O^2 runtime, but even for
35+ /// a struct with hundreds of fields all clashing with increasing _ prefixes, which is a highly
36+ /// degenerate example input, it is fine.
37+ fn fresh_name (
38+ fields : & syn:: punctuated:: Punctuated < syn:: Field , syn:: token:: Comma > ,
39+ mut target : proc_macro2:: Ident ,
40+ ) -> Ident {
41+ while fields. iter ( ) . any ( |f| {
42+ f. ident
43+ . as_ref ( )
44+ . map ( |ident| ident == & target)
45+ . unwrap_or ( false )
46+ } ) {
47+ target = format_ident ! ( "_{target}" ) ;
48+ }
49+ target
50+ }
851
952fn extract_lifetime (
1053 gp : & syn:: punctuated:: Punctuated < syn:: GenericParam , syn:: token:: Comma > ,
@@ -33,21 +76,31 @@ fn extract_lifetime(
3376fn impl_field (
3477 ident : & proc_macro2:: TokenStream ,
3578 ty : & syn:: Type ,
79+ src : & proc_macro2:: Ident ,
80+ default_ctx : & proc_macro2:: TokenStream ,
3681 custom_ctx : Option < & proc_macro2:: TokenStream > ,
82+ offset : & Ident ,
3783 noctx : bool ,
3884) -> proc_macro2:: TokenStream {
39- let default_ctx = syn:: Ident :: new ( "ctx" , proc_macro2:: Span :: call_site ( ) ) . into_token_stream ( ) ;
40- let ctx = custom_ctx. unwrap_or ( & default_ctx) ;
85+ let ctx = custom_ctx. unwrap_or ( default_ctx) ;
4186 match ty {
42- syn:: Type :: Group ( group) => impl_field ( ident, & group. elem , custom_ctx, noctx) ,
87+ syn:: Type :: Group ( group) => impl_field (
88+ ident,
89+ & group. elem ,
90+ src,
91+ default_ctx,
92+ custom_ctx,
93+ offset,
94+ noctx,
95+ ) ,
4396 _ => {
4497 if noctx {
4598 quote ! {
46- let #ident = src. gread:: <#ty>( offset) ?;
99+ let #ident = # src. gread:: <#ty>( # offset) ?;
47100 }
48101 } else {
49102 quote ! {
50- let #ident = src. gread_with:: <#ty>( offset, #ctx) ?;
103+ let #ident = # src. gread_with:: <#ty>( # offset, #ctx) ?;
51104 }
52105 }
53106 }
@@ -107,6 +160,19 @@ fn impl_struct(
107160 generics : & syn:: Generics ,
108161 unnamed : bool ,
109162) -> proc_macro2:: TokenStream {
163+ let offset = fresh_name (
164+ fields,
165+ syn:: Ident :: new ( "offset" , proc_macro2:: Span :: call_site ( ) ) ,
166+ ) ;
167+ let src = fresh_name (
168+ fields,
169+ syn:: Ident :: new ( "src" , proc_macro2:: Span :: call_site ( ) ) ,
170+ ) ;
171+ let ctx = fresh_name (
172+ fields,
173+ syn:: Ident :: new ( "ctx" , proc_macro2:: Span :: call_site ( ) ) ,
174+ )
175+ . to_token_stream ( ) ;
110176 let ( items, item_assignments) = fields
111177 . iter ( )
112178 . enumerate ( )
@@ -130,7 +196,15 @@ fn impl_struct(
130196 let mut noctx = false ;
131197 let custom_ctx = custom_ctx ( f, & mut noctx) ;
132198 (
133- impl_field ( & prefixed_ident, ty, custom_ctx. as_ref ( ) , noctx) ,
199+ impl_field (
200+ & prefixed_ident,
201+ ty,
202+ & src,
203+ & ctx,
204+ custom_ctx. as_ref ( ) ,
205+ & offset,
206+ noctx,
207+ ) ,
134208 quote ! { #ident: #prefixed_ident } ,
135209 )
136210 } )
@@ -192,12 +266,11 @@ fn impl_struct(
192266 // TODO: allow passing user error here
193267 type Error = :: scroll:: Error ;
194268 #[ inline]
195- fn try_from_ctx( src: & #lifetime [ u8 ] , ctx: :: scroll:: Endian ) -> :: scroll:: export:: result:: Result <( Self , usize ) , Self :: Error > {
269+ fn try_from_ctx( # src: & #lifetime [ u8 ] , # ctx: :: scroll:: Endian ) -> :: scroll:: export:: result:: Result <( Self , usize ) , Self :: Error > {
196270 use :: scroll:: Pread ;
197- let offset = & mut 0 ;
271+ let # offset = & mut 0 ;
198272 #( #items) *
199- let data = Self { #( #item_assignments, ) * } ;
200- Ok ( ( data, * offset) )
273+ Ok ( ( Self { #( #item_assignments, ) * } , * #offset) )
201274 }
202275 }
203276 }
@@ -219,7 +292,7 @@ fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
219292}
220293
221294#[ proc_macro_derive( Pread , attributes( scroll) ) ]
222- pub fn derive_pread ( input : TokenStream ) -> TokenStream {
295+ pub fn derive_pread ( input : proc_macro :: TokenStream ) -> proc_macro :: TokenStream {
223296 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
224297 let generated = impl_try_from_ctx ( & ast) ;
225298 generated. into ( )
@@ -228,11 +301,12 @@ pub fn derive_pread(input: TokenStream) -> TokenStream {
228301fn impl_pwrite_field (
229302 ident : & proc_macro2:: TokenStream ,
230303 ty : & syn:: Type ,
304+ default_ctx : & proc_macro2:: TokenStream ,
231305 custom_ctx : Option < & proc_macro2:: TokenStream > ,
306+ offset : & proc_macro2:: Ident ,
232307 noctx : bool ,
233308) -> proc_macro2:: TokenStream {
234- let default_ctx = syn:: Ident :: new ( "ctx" , proc_macro2:: Span :: call_site ( ) ) . into_token_stream ( ) ;
235- let ctx = custom_ctx. unwrap_or ( & default_ctx) ;
309+ let ctx = custom_ctx. unwrap_or ( default_ctx) ;
236310 match ty {
237311 syn:: Type :: Array ( array) => match & array. len {
238312 syn:: Expr :: Lit ( syn:: ExprLit {
@@ -242,39 +316,41 @@ fn impl_pwrite_field(
242316 let size = int. base10_parse :: < usize > ( ) . unwrap ( ) ;
243317 quote ! {
244318 for i in 0 ..#size {
245- dst. gwrite_with( & self . #ident[ i] , offset, #ctx) ?;
319+ dst. gwrite_with( & self . #ident[ i] , # offset, #ctx) ?;
246320 }
247321 }
248322 }
249323 _ => panic ! ( "Pwrite derive with bad array constexpr" ) ,
250324 } ,
251- syn:: Type :: Group ( group) => impl_pwrite_field ( ident, & group. elem , custom_ctx, noctx) ,
325+ syn:: Type :: Group ( group) => {
326+ impl_pwrite_field ( ident, & group. elem , default_ctx, custom_ctx, offset, noctx)
327+ }
252328 syn:: Type :: Reference ( reference) => match * reference. elem {
253329 syn:: Type :: Slice ( _) => {
254330 quote ! {
255- dst. gwrite_with( self . #ident, offset, ( ) ) ?
331+ dst. gwrite_with( self . #ident, # offset, ( ) ) ?
256332 }
257333 }
258334 syn:: Type :: Path ( ref path) => {
259335 if path. path . get_ident ( ) . unwrap ( ) . to_string ( ) . as_str ( ) == "str" {
260336 quote ! {
261- dst. gwrite( self . #ident, offset) ?
337+ dst. gwrite( self . #ident, # offset) ?
262338 }
263339 } else {
264340 quote ! {
265- dst. gwrite_with( self . #ident, offset, #ctx) ?
341+ dst. gwrite_with( self . #ident, # offset, #ctx) ?
266342 }
267343 }
268344 }
269345 _ => {
270346 quote ! {
271- dst. gwrite_with( self . #ident, offset, #ctx) ?
347+ dst. gwrite_with( self . #ident, # offset, #ctx) ?
272348 }
273349 }
274350 } ,
275351 _ => {
276352 quote ! {
277- dst. gwrite_with( & self . #ident, offset, #ctx) ?
353+ dst. gwrite_with( & self . #ident, # offset, #ctx) ?
278354 }
279355 }
280356 }
@@ -285,18 +361,19 @@ fn impl_try_into_ctx(
285361 fields : & syn:: punctuated:: Punctuated < syn:: Field , syn:: Token ![ , ] > ,
286362 generics : & syn:: Generics ,
287363) -> proc_macro2:: TokenStream {
288- let items: Vec < _ > = fields
364+ let ( idents, offset) = extract_idents_and_offset ( fields) ;
365+ let ctx = fresh_name (
366+ fields,
367+ syn:: Ident :: new ( "ctx" , proc_macro2:: Span :: call_site ( ) ) ,
368+ )
369+ . to_token_stream ( ) ;
370+ let items: Vec < _ > = idents
289371 . iter ( )
290- . enumerate ( )
291- . map ( |( i, f) | {
292- let ident = & f. ident . as_ref ( ) . map ( |i| quote ! { #i} ) . unwrap_or ( {
293- let t = proc_macro2:: Literal :: usize_unsuffixed ( i) ;
294- quote ! { #t}
295- } ) ;
372+ . map ( |( ident, f) | {
296373 let ty = & f. ty ;
297374 let mut noctx = false ;
298375 let custom_ctx = custom_ctx ( f, & mut noctx) ;
299- impl_pwrite_field ( ident, ty, custom_ctx. as_ref ( ) , noctx)
376+ impl_pwrite_field ( ident, ty, & ctx , custom_ctx. as_ref ( ) , & offset , noctx)
300377 } )
301378 . collect ( ) ;
302379
@@ -356,11 +433,11 @@ fn impl_try_into_ctx(
356433 impl <#fresh_lifetime, #gp > :: scroll:: ctx:: TryIntoCtx <:: scroll:: Endian > for & #fresh_lifetime #name #gn #gwref {
357434 type Error = :: scroll:: Error ;
358435 #[ inline]
359- fn try_into_ctx( self , dst: & mut [ u8 ] , ctx: :: scroll:: Endian ) -> :: scroll:: export:: result:: Result <usize , Self :: Error > {
436+ fn try_into_ctx( self , dst: & mut [ u8 ] , # ctx: :: scroll:: Endian ) -> :: scroll:: export:: result:: Result <usize , Self :: Error > {
360437 use :: scroll:: Pwrite ;
361- let offset = & mut 0 ;
438+ let # offset = & mut 0 ;
362439 #( #items; ) *
363- Ok ( * offset)
440+ Ok ( * # offset)
364441 }
365442 }
366443
@@ -390,7 +467,7 @@ fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
390467}
391468
392469#[ proc_macro_derive( Pwrite , attributes( scroll) ) ]
393- pub fn derive_pwrite ( input : TokenStream ) -> TokenStream {
470+ pub fn derive_pwrite ( input : proc_macro :: TokenStream ) -> proc_macro :: TokenStream {
394471 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
395472 let generated = impl_pwrite ( & ast) ;
396473 generated. into ( )
@@ -493,7 +570,7 @@ fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
493570}
494571
495572#[ proc_macro_derive( SizeWith , attributes( scroll) ) ]
496- pub fn derive_sizewith ( input : TokenStream ) -> TokenStream {
573+ pub fn derive_sizewith ( input : proc_macro :: TokenStream ) -> proc_macro :: TokenStream {
497574 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
498575 let generated = impl_size_with ( & ast) ;
499576 generated. into ( )
@@ -600,7 +677,7 @@ fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
600677}
601678
602679#[ proc_macro_derive( IOread , attributes( scroll) ) ]
603- pub fn derive_ioread ( input : TokenStream ) -> TokenStream {
680+ pub fn derive_ioread ( input : proc_macro :: TokenStream ) -> proc_macro :: TokenStream {
604681 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
605682 let generated = impl_from_ctx ( & ast) ;
606683 generated. into ( )
@@ -711,7 +788,7 @@ fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
711788}
712789
713790#[ proc_macro_derive( IOwrite , attributes( scroll) ) ]
714- pub fn derive_iowrite ( input : TokenStream ) -> TokenStream {
791+ pub fn derive_iowrite ( input : proc_macro :: TokenStream ) -> proc_macro :: TokenStream {
715792 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
716793 let generated = impl_iowrite ( & ast) ;
717794 generated. into ( )
0 commit comments