55//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
66
77use crate :: {
8- Delimiter , Group , Ident , Literal , Punct , Spacing , Span , ToTokens , TokenStream , TokenTree ,
8+ BitOr , Delimiter , Group , Ident , Literal , Punct , Spacing , Span , ToTokens , TokenStream , TokenTree ,
99} ;
1010
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+
11185macro_rules! minimal_quote_tt {
12186 ( ( $( $t: tt) * ) ) => { Group :: new( Delimiter :: Parenthesis , minimal_quote!( $( $t) * ) ) } ;
13187 ( [ $( $t: tt) * ] ) => { Group :: new( Delimiter :: Bracket , minimal_quote!( $( $t) * ) ) } ;
@@ -20,7 +194,13 @@ macro_rules! minimal_quote_tt {
20194 ( >) => { Punct :: new( '>' , Spacing :: Alone ) } ;
21195 ( & ) => { Punct :: new( '&' , Spacing :: Alone ) } ;
22196 ( =) => { 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( ) ) } ;
23202 ( $i: ident) => { Ident :: new( stringify!( $i) , Span :: def_site( ) ) } ;
203+ ( $lit: literal) => { stringify!( $lit) . parse:: <Literal >( ) . unwrap( ) } ;
24204}
25205
26206macro_rules! minimal_quote_ts {
@@ -36,6 +216,39 @@ macro_rules! minimal_quote_ts {
36216 [ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
37217 }
38218 } ;
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+ } ;
39252 ( $t: tt) => { TokenTree :: from( minimal_quote_tt!( $t) ) } ;
40253}
41254
@@ -71,17 +284,99 @@ pub fn quote(stream: TokenStream) -> TokenStream {
71284 let mut after_dollar = false ;
72285
73286 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 ( ) {
75289 if after_dollar {
76290 after_dollar = false ;
77291 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+ }
78371 TokenTree :: Ident ( _) => {
79372 minimal_quote ! ( crate :: ToTokens :: to_tokens( & ( @ tree) , & mut ts) ; )
80373 . to_tokens ( & mut tokens) ;
81374 continue ;
82375 }
83376 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+ ) ,
85380 }
86381 } else if let TokenTree :: Punct ( ref tt) = tree {
87382 if tt. as_char ( ) == '$' {
@@ -155,6 +450,33 @@ pub fn quote(stream: TokenStream) -> TokenStream {
155450 }
156451}
157452
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+
158480/// Quote a `Span` into a `TokenStream`.
159481/// This is needed to implement a custom quoter.
160482#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
0 commit comments