5
5
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
6
6
7
7
use crate :: {
8
- Delimiter , Group , Ident , Literal , Punct , Spacing , Span , ToTokens , TokenStream , TokenTree ,
8
+ BitOr , Delimiter , Group , Ident , Literal , Punct , Spacing , Span , ToTokens , TokenStream , TokenTree ,
9
9
} ;
10
10
11
+ #[ doc( hidden) ]
12
+ pub struct HasIterator ; // True
13
+ #[ doc( hidden) ]
14
+ pub struct ThereIsNoIteratorInRepetition ; // False
15
+
16
+ impl BitOr < ThereIsNoIteratorInRepetition > for ThereIsNoIteratorInRepetition {
17
+ type Output = ThereIsNoIteratorInRepetition ;
18
+ fn bitor ( self , _rhs : ThereIsNoIteratorInRepetition ) -> ThereIsNoIteratorInRepetition {
19
+ ThereIsNoIteratorInRepetition
20
+ }
21
+ }
22
+
23
+ impl BitOr < ThereIsNoIteratorInRepetition > for HasIterator {
24
+ type Output = HasIterator ;
25
+ fn bitor ( self , _rhs : ThereIsNoIteratorInRepetition ) -> HasIterator {
26
+ HasIterator
27
+ }
28
+ }
29
+
30
+ impl BitOr < HasIterator > for ThereIsNoIteratorInRepetition {
31
+ type Output = HasIterator ;
32
+ fn bitor ( self , _rhs : HasIterator ) -> HasIterator {
33
+ HasIterator
34
+ }
35
+ }
36
+
37
+ impl BitOr < HasIterator > for HasIterator {
38
+ type Output = HasIterator ;
39
+ fn bitor ( self , _rhs : HasIterator ) -> HasIterator {
40
+ HasIterator
41
+ }
42
+ }
43
+
44
+ /// Extension traits used by the implementation of `quote!`. These are defined
45
+ /// in separate traits, rather than as a single trait due to ambiguity issues.
46
+ ///
47
+ /// These traits expose a `quote_into_iter` method which should allow calling
48
+ /// whichever impl happens to be applicable. Calling that method repeatedly on
49
+ /// the returned value should be idempotent.
50
+ #[ doc( hidden) ]
51
+ pub mod ext {
52
+ use core:: slice;
53
+ use std:: collections:: btree_set:: { self , BTreeSet } ;
54
+
55
+ use super :: {
56
+ HasIterator as HasIter , RepInterp , ThereIsNoIteratorInRepetition as DoesNotHaveIter ,
57
+ } ;
58
+ use crate :: ToTokens ;
59
+
60
+ /// Extension trait providing the `quote_into_iter` method on iterators.
61
+ #[ doc( hidden) ]
62
+ pub trait RepIteratorExt : Iterator + Sized {
63
+ fn quote_into_iter ( self ) -> ( Self , HasIter ) {
64
+ ( self , HasIter )
65
+ }
66
+ }
67
+
68
+ impl < T : Iterator > RepIteratorExt for T { }
69
+
70
+ /// Extension trait providing the `quote_into_iter` method for
71
+ /// non-iterable types. These types interpolate the same value in each
72
+ /// iteration of the repetition.
73
+ #[ doc( hidden) ]
74
+ pub trait RepToTokensExt {
75
+ /// Pretend to be an iterator for the purposes of `quote_into_iter`.
76
+ /// This allows repeated calls to `quote_into_iter` to continue
77
+ /// correctly returning DoesNotHaveIter.
78
+ fn next ( & self ) -> Option < & Self > {
79
+ Some ( self )
80
+ }
81
+
82
+ fn quote_into_iter ( & self ) -> ( & Self , DoesNotHaveIter ) {
83
+ ( self , DoesNotHaveIter )
84
+ }
85
+ }
86
+
87
+ impl < T : ToTokens + ?Sized > RepToTokensExt for T { }
88
+
89
+ /// Extension trait providing the `quote_into_iter` method for types that
90
+ /// can be referenced as an iterator.
91
+ #[ doc( hidden) ]
92
+ pub trait RepAsIteratorExt < ' q > {
93
+ type Iter : Iterator ;
94
+
95
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) ;
96
+ }
97
+
98
+ impl < ' q , T : RepAsIteratorExt < ' q > + ?Sized > RepAsIteratorExt < ' q > for & T {
99
+ type Iter = T :: Iter ;
100
+
101
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
102
+ <T as RepAsIteratorExt >:: quote_into_iter ( * self )
103
+ }
104
+ }
105
+
106
+ impl < ' q , T : RepAsIteratorExt < ' q > + ?Sized > RepAsIteratorExt < ' q > for & mut T {
107
+ type Iter = T :: Iter ;
108
+
109
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
110
+ <T as RepAsIteratorExt >:: quote_into_iter ( * self )
111
+ }
112
+ }
113
+
114
+ impl < ' q , T : ' q > RepAsIteratorExt < ' q > for [ T ] {
115
+ type Iter = slice:: Iter < ' q , T > ;
116
+
117
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
118
+ ( self . iter ( ) , HasIter )
119
+ }
120
+ }
121
+
122
+ impl < ' q , T : ' q , const N : usize > RepAsIteratorExt < ' q > for [ T ; N ] {
123
+ type Iter = slice:: Iter < ' q , T > ;
124
+
125
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
126
+ ( self . iter ( ) , HasIter )
127
+ }
128
+ }
129
+
130
+ impl < ' q , T : ' q > RepAsIteratorExt < ' q > for Vec < T > {
131
+ type Iter = slice:: Iter < ' q , T > ;
132
+
133
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
134
+ ( self . iter ( ) , HasIter )
135
+ }
136
+ }
137
+
138
+ impl < ' q , T : ' q > RepAsIteratorExt < ' q > for BTreeSet < T > {
139
+ type Iter = btree_set:: Iter < ' q , T > ;
140
+
141
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
142
+ ( self . iter ( ) , HasIter )
143
+ }
144
+ }
145
+
146
+ impl < ' q , T : RepAsIteratorExt < ' q > > RepAsIteratorExt < ' q > for RepInterp < T > {
147
+ type Iter = T :: Iter ;
148
+
149
+ fn quote_into_iter ( & ' q self ) -> ( Self :: Iter , HasIter ) {
150
+ self . 0 . quote_into_iter ( )
151
+ }
152
+ }
153
+ }
154
+
155
+ // Helper type used within interpolations to allow for repeated binding names.
156
+ // Implements the relevant traits, and exports a dummy `next()` method.
157
+ #[ derive( Copy , Clone ) ]
158
+ #[ doc( hidden) ]
159
+ pub struct RepInterp < T > ( pub T ) ;
160
+
161
+ impl < T > RepInterp < T > {
162
+ // This method is intended to look like `Iterator::next`, and is called when
163
+ // a name is bound multiple times, as the previous binding will shadow the
164
+ // original `Iterator` object. This allows us to avoid advancing the
165
+ // iterator multiple times per iteration.
166
+ pub fn next ( self ) -> Option < T > {
167
+ Some ( self . 0 )
168
+ }
169
+ }
170
+
171
+ impl < T : Iterator > Iterator for RepInterp < T > {
172
+ type Item = T :: Item ;
173
+
174
+ fn next ( & mut self ) -> Option < Self :: Item > {
175
+ self . 0 . next ( )
176
+ }
177
+ }
178
+
179
+ impl < T : ToTokens > ToTokens for RepInterp < T > {
180
+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
181
+ self . 0 . to_tokens ( tokens) ;
182
+ }
183
+ }
184
+
11
185
macro_rules! minimal_quote_tt {
12
186
( ( $( $t: tt) * ) ) => { Group :: new( Delimiter :: Parenthesis , minimal_quote!( $( $t) * ) ) } ;
13
187
( [ $( $t: tt) * ] ) => { Group :: new( Delimiter :: Bracket , minimal_quote!( $( $t) * ) ) } ;
@@ -20,7 +194,13 @@ macro_rules! minimal_quote_tt {
20
194
( >) => { Punct :: new( '>' , Spacing :: Alone ) } ;
21
195
( & ) => { Punct :: new( '&' , Spacing :: Alone ) } ;
22
196
( =) => { Punct :: new( '=' , Spacing :: Alone ) } ;
197
+ ( #) => { Punct :: new( '#' , Spacing :: Alone ) } ;
198
+ ( |) => { Punct :: new( '|' , Spacing :: Alone ) } ;
199
+ ( : ) => { Punct :: new( ':' , Spacing :: Alone ) } ;
200
+ ( * ) => { Punct :: new( '*' , Spacing :: Alone ) } ;
201
+ ( _) => { Ident :: new( "_" , Span :: def_site( ) ) } ;
23
202
( $i: ident) => { Ident :: new( stringify!( $i) , Span :: def_site( ) ) } ;
203
+ ( $lit: literal) => { stringify!( $lit) . parse:: <Literal >( ) . unwrap( ) } ;
24
204
}
25
205
26
206
macro_rules! minimal_quote_ts {
@@ -36,6 +216,39 @@ macro_rules! minimal_quote_ts {
36
216
[ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
37
217
}
38
218
} ;
219
+ ( =>) => {
220
+ {
221
+ let mut c = (
222
+ TokenTree :: from( Punct :: new( '=' , Spacing :: Joint ) ) ,
223
+ TokenTree :: from( Punct :: new( '>' , Spacing :: Alone ) )
224
+ ) ;
225
+ c. 0 . set_span( Span :: def_site( ) ) ;
226
+ c. 1 . set_span( Span :: def_site( ) ) ;
227
+ [ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
228
+ }
229
+ } ;
230
+ ( +=) => {
231
+ {
232
+ let mut c = (
233
+ TokenTree :: from( Punct :: new( '+' , Spacing :: Joint ) ) ,
234
+ TokenTree :: from( Punct :: new( '=' , Spacing :: Alone ) )
235
+ ) ;
236
+ c. 0 . set_span( Span :: def_site( ) ) ;
237
+ c. 1 . set_span( Span :: def_site( ) ) ;
238
+ [ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
239
+ }
240
+ } ;
241
+ ( !=) => {
242
+ {
243
+ let mut c = (
244
+ TokenTree :: from( Punct :: new( '!' , Spacing :: Joint ) ) ,
245
+ TokenTree :: from( Punct :: new( '=' , Spacing :: Alone ) )
246
+ ) ;
247
+ c. 0 . set_span( Span :: def_site( ) ) ;
248
+ c. 1 . set_span( Span :: def_site( ) ) ;
249
+ [ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
250
+ }
251
+ } ;
39
252
( $t: tt) => { TokenTree :: from( minimal_quote_tt!( $t) ) } ;
40
253
}
41
254
@@ -71,17 +284,99 @@ pub fn quote(stream: TokenStream) -> TokenStream {
71
284
let mut after_dollar = false ;
72
285
73
286
let mut tokens = crate :: TokenStream :: new ( ) ;
74
- for tree in stream {
287
+ let mut iter = stream. into_iter ( ) . peekable ( ) ;
288
+ while let Some ( tree) = iter. next ( ) {
75
289
if after_dollar {
76
290
after_dollar = false ;
77
291
match tree {
292
+ TokenTree :: Group ( tt) => {
293
+ // Handles repetition by expanding`$( CONTENTS ) SEP_OPT *` to `{ REP_EXPANDED }`.
294
+ let contents = tt. stream ( ) ;
295
+
296
+ // The `*` token is also consumed here.
297
+ let sep_opt: Option < Punct > = match ( iter. next ( ) , iter. peek ( ) ) {
298
+ ( Some ( TokenTree :: Punct ( sep) ) , Some ( TokenTree :: Punct ( star) ) )
299
+ if sep. spacing ( ) == Spacing :: Joint && star. as_char ( ) == '*' =>
300
+ {
301
+ iter. next ( ) ;
302
+ Some ( sep)
303
+ }
304
+ ( Some ( TokenTree :: Punct ( star) ) , _) if star. as_char ( ) == '*' => None ,
305
+ _ => panic ! ( "`$(...)` must be followed by `*` in `quote!`" ) ,
306
+ } ;
307
+
308
+ let mut rep_expanded = TokenStream :: new ( ) ;
309
+
310
+ // Append setup code for a `while`, where recursively quoted `CONTENTS`
311
+ // and `SEP_OPT` are expanded, to `REP_EXPANDED`.
312
+ let meta_vars = collect_meta_vars ( contents. clone ( ) ) ;
313
+ minimal_quote ! (
314
+ use crate :: ext:: * ;
315
+ ( @ if sep_opt. is_some( ) {
316
+ minimal_quote!( let mut _i = 0usize ; )
317
+ } else {
318
+ minimal_quote!( ( ) ; )
319
+ } )
320
+ let has_iter = crate :: ThereIsNoIteratorInRepetition ;
321
+ )
322
+ . to_tokens ( & mut rep_expanded) ;
323
+ for meta_var in & meta_vars {
324
+ minimal_quote ! (
325
+ #[ allow( unused_mut) ]
326
+ let ( mut ( @ meta_var) , i) = ( @ meta_var) . quote_into_iter( ) ;
327
+ let has_iter = has_iter | i;
328
+ )
329
+ . to_tokens ( & mut rep_expanded) ;
330
+ }
331
+ minimal_quote ! ( let _: crate :: HasIterator = has_iter; )
332
+ . to_tokens ( & mut rep_expanded) ;
333
+
334
+ // Append the `while` to `REP_EXPANDED`.
335
+ let mut while_body = TokenStream :: new ( ) ;
336
+ for meta_var in & meta_vars {
337
+ minimal_quote ! (
338
+ let ( @ meta_var) = match ( @ meta_var) . next( ) {
339
+ Some ( _x) => crate :: RepInterp ( _x) ,
340
+ None => break ,
341
+ } ;
342
+ )
343
+ . to_tokens ( & mut while_body) ;
344
+ }
345
+ minimal_quote ! (
346
+ ( @ if let Some ( sep) = sep_opt {
347
+ minimal_quote!(
348
+ if _i > 0 {
349
+ ( @ minimal_quote!( crate :: ToTokens :: to_tokens( & crate :: TokenTree :: Punct ( crate :: Punct :: new(
350
+ ( @ TokenTree :: from( Literal :: character( sep. as_char( ) ) ) ) ,
351
+ ( @ minimal_quote!( crate :: Spacing :: Alone ) ) ,
352
+ ) ) , & mut ts) ; ) )
353
+ }
354
+ _i += 1 ;
355
+ )
356
+ } else {
357
+ minimal_quote!( ( ) ; )
358
+ } )
359
+ ( @ quote( contents. clone( ) ) ) to_tokens( & mut ts) ;
360
+ )
361
+ . to_tokens ( & mut while_body) ;
362
+ rep_expanded. extend ( vec ! [
363
+ TokenTree :: Ident ( Ident :: new( "while" , Span :: call_site( ) ) ) ,
364
+ TokenTree :: Ident ( Ident :: new( "true" , Span :: call_site( ) ) ) ,
365
+ TokenTree :: Group ( Group :: new( Delimiter :: Brace , while_body) ) ,
366
+ ] ) ;
367
+
368
+ minimal_quote ! ( ( @ TokenTree :: Group ( Group :: new( Delimiter :: Brace , rep_expanded) ) ) ) . to_tokens ( & mut tokens) ;
369
+ continue ;
370
+ }
78
371
TokenTree :: Ident ( _) => {
79
372
minimal_quote ! ( crate :: ToTokens :: to_tokens( & ( @ tree) , & mut ts) ; )
80
373
. to_tokens ( & mut tokens) ;
81
374
continue ;
82
375
}
83
376
TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
84
- _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
377
+ _ => panic ! (
378
+ "`$` must be followed by an ident or `$` or a repetition group in `quote!`"
379
+ ) ,
85
380
}
86
381
} else if let TokenTree :: Punct ( ref tt) = tree {
87
382
if tt. as_char ( ) == '$' {
@@ -155,6 +450,33 @@ pub fn quote(stream: TokenStream) -> TokenStream {
155
450
}
156
451
}
157
452
453
+ /// Helper function to support macro repetitions like `$( CONTENTS ) SEP_OPT *` in `quote!`.
454
+ /// Recursively collects all `Ident`s (meta-variables) that follow a `$`
455
+ /// from the given `CONTENTS` stream, preserving their order of appearance.
456
+ fn collect_meta_vars ( content_stream : TokenStream ) -> Vec < Ident > {
457
+ fn helper ( stream : TokenStream , out : & mut Vec < Ident > ) {
458
+ let mut iter = stream. into_iter ( ) . peekable ( ) ;
459
+ while let Some ( tree) = iter. next ( ) {
460
+ match & tree {
461
+ TokenTree :: Punct ( tt) if tt. as_char ( ) == '$' => {
462
+ if let Some ( TokenTree :: Ident ( id) ) = iter. peek ( ) {
463
+ out. push ( id. clone ( ) ) ;
464
+ iter. next ( ) ;
465
+ }
466
+ }
467
+ TokenTree :: Group ( tt) => {
468
+ helper ( tt. stream ( ) , out) ;
469
+ }
470
+ _ => { }
471
+ }
472
+ }
473
+ }
474
+
475
+ let mut vars = Vec :: new ( ) ;
476
+ helper ( content_stream, & mut vars) ;
477
+ vars
478
+ }
479
+
158
480
/// Quote a `Span` into a `TokenStream`.
159
481
/// This is needed to implement a custom quoter.
160
482
#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
0 commit comments