4
4
//! This quasiquoter uses macros 2.0 hygiene to reliably access
5
5
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
6
6
7
- use crate :: { Delimiter , Group , Ident , Literal , Punct , Spacing , Span , TokenStream , TokenTree } ;
7
+ use crate :: {
8
+ Delimiter , Group , Ident , Literal , Punct , Spacing , Span , ToTokens , TokenStream , TokenTree ,
9
+ } ;
8
10
9
- macro_rules! quote_tt {
10
- ( ( $( $t: tt) * ) ) => { Group :: new( Delimiter :: Parenthesis , quote !( $( $t) * ) ) } ;
11
- ( [ $( $t: tt) * ] ) => { Group :: new( Delimiter :: Bracket , quote !( $( $t) * ) ) } ;
12
- ( { $( $t: tt) * } ) => { Group :: new( Delimiter :: Brace , quote !( $( $t) * ) ) } ;
11
+ macro_rules! minimal_quote_tt {
12
+ ( ( $( $t: tt) * ) ) => { Group :: new( Delimiter :: Parenthesis , minimal_quote !( $( $t) * ) ) } ;
13
+ ( [ $( $t: tt) * ] ) => { Group :: new( Delimiter :: Bracket , minimal_quote !( $( $t) * ) ) } ;
14
+ ( { $( $t: tt) * } ) => { Group :: new( Delimiter :: Brace , minimal_quote !( $( $t) * ) ) } ;
13
15
( , ) => { Punct :: new( ',' , Spacing :: Alone ) } ;
14
16
( . ) => { Punct :: new( '.' , Spacing :: Alone ) } ;
15
17
( ; ) => { Punct :: new( ';' , Spacing :: Alone ) } ;
@@ -21,21 +23,20 @@ macro_rules! quote_tt {
21
23
( $i: ident) => { Ident :: new( stringify!( $i) , Span :: def_site( ) ) } ;
22
24
}
23
25
24
- macro_rules! quote_ts {
26
+ macro_rules! minimal_quote_ts {
25
27
( ( @ $( $t: tt) * ) ) => { $( $t) * } ;
26
28
( :: ) => {
27
- [
28
- TokenTree :: from( Punct :: new( ':' , Spacing :: Joint ) ) ,
29
- TokenTree :: from( Punct :: new( ':' , Spacing :: Alone ) ) ,
30
- ] . iter( )
31
- . cloned( )
32
- . map( |mut x| {
33
- x. set_span( Span :: def_site( ) ) ;
34
- x
35
- } )
36
- . collect:: <TokenStream >( )
29
+ {
30
+ let mut c = (
31
+ TokenTree :: from( Punct :: new( ':' , Spacing :: Joint ) ) ,
32
+ TokenTree :: from( Punct :: new( ':' , Spacing :: Alone ) )
33
+ ) ;
34
+ c. 0 . set_span( Span :: def_site( ) ) ;
35
+ c. 1 . set_span( Span :: def_site( ) ) ;
36
+ [ c. 0 , c. 1 ] . into_iter( ) . collect:: <TokenStream >( )
37
+ }
37
38
} ;
38
- ( $t: tt) => { TokenTree :: from( quote_tt !( $t) ) } ;
39
+ ( $t: tt) => { TokenTree :: from( minimal_quote_tt !( $t) ) } ;
39
40
}
40
41
41
42
/// Simpler version of the real `quote!` macro, implemented solely
@@ -46,12 +47,14 @@ macro_rules! quote_ts {
46
47
///
47
48
/// Note: supported tokens are a subset of the real `quote!`, but
48
49
/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
49
- macro_rules! quote {
50
- ( ) => { TokenStream :: new( ) } ;
50
+ macro_rules! minimal_quote {
51
51
( $( $t: tt) * ) => {
52
- [
53
- $( TokenStream :: from( quote_ts!( $t) ) , ) *
54
- ] . iter( ) . cloned( ) . collect:: <TokenStream >( )
52
+ {
53
+ #[ allow( unused_mut) ] // In case the expansion is empty
54
+ let mut ts = TokenStream :: new( ) ;
55
+ $( ToTokens :: to_tokens( & minimal_quote_ts!( $t) , & mut ts) ; ) *
56
+ ts
57
+ }
55
58
} ;
56
59
}
57
60
@@ -62,52 +65,66 @@ macro_rules! quote {
62
65
#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
63
66
pub fn quote ( stream : TokenStream ) -> TokenStream {
64
67
if stream. is_empty ( ) {
65
- return quote ! ( crate :: TokenStream :: new( ) ) ;
68
+ return minimal_quote ! ( crate :: TokenStream :: new( ) ) ;
66
69
}
67
- let proc_macro_crate = quote ! ( crate ) ;
70
+ let proc_macro_crate = minimal_quote ! ( crate ) ;
68
71
let mut after_dollar = false ;
69
- let tokens = stream
70
- . into_iter ( )
71
- . filter_map ( |tree| {
72
- if after_dollar {
73
- after_dollar = false ;
74
- match tree {
75
- TokenTree :: Ident ( _) => {
76
- return Some ( quote ! ( Into :: <crate :: TokenStream >:: into(
77
- Clone :: clone( & ( @ tree) ) ) , ) ) ;
78
- }
79
- TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
80
- _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
81
- }
82
- } else if let TokenTree :: Punct ( ref tt) = tree {
83
- if tt. as_char ( ) == '$' {
84
- after_dollar = true ;
85
- return None ;
72
+
73
+ let mut tokens = crate :: TokenStream :: new ( ) ;
74
+ for tree in stream {
75
+ if after_dollar {
76
+ after_dollar = false ;
77
+ match tree {
78
+ TokenTree :: Ident ( _) => {
79
+ minimal_quote ! ( crate :: ToTokens :: to_tokens( & ( @ tree) , & mut ts) ; )
80
+ . to_tokens ( & mut tokens) ;
81
+ continue ;
86
82
}
83
+ TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
84
+ _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
87
85
}
86
+ } else if let TokenTree :: Punct ( ref tt) = tree {
87
+ if tt. as_char ( ) == '$' {
88
+ after_dollar = true ;
89
+ continue ;
90
+ }
91
+ }
88
92
89
- Some ( quote ! ( crate :: TokenStream :: from( ( @ match tree {
90
- TokenTree :: Punct ( tt) => quote!( crate :: TokenTree :: Punct ( crate :: Punct :: new(
93
+ match tree {
94
+ TokenTree :: Punct ( tt) => {
95
+ minimal_quote ! ( crate :: ToTokens :: to_tokens( & crate :: TokenTree :: Punct ( crate :: Punct :: new(
91
96
( @ TokenTree :: from( Literal :: character( tt. as_char( ) ) ) ) ,
92
97
( @ match tt. spacing( ) {
93
- Spacing :: Alone => quote !( crate :: Spacing :: Alone ) ,
94
- Spacing :: Joint => quote !( crate :: Spacing :: Joint ) ,
98
+ Spacing :: Alone => minimal_quote !( crate :: Spacing :: Alone ) ,
99
+ Spacing :: Joint => minimal_quote !( crate :: Spacing :: Joint ) ,
95
100
} ) ,
96
- ) ) ) ,
97
- TokenTree :: Group ( tt) => quote!( crate :: TokenTree :: Group ( crate :: Group :: new(
101
+ ) ) , & mut ts) ; )
102
+ }
103
+ TokenTree :: Group ( tt) => {
104
+ minimal_quote ! ( crate :: ToTokens :: to_tokens( & crate :: TokenTree :: Group ( crate :: Group :: new(
98
105
( @ match tt. delimiter( ) {
99
- Delimiter :: Parenthesis => quote !( crate :: Delimiter :: Parenthesis ) ,
100
- Delimiter :: Brace => quote !( crate :: Delimiter :: Brace ) ,
101
- Delimiter :: Bracket => quote !( crate :: Delimiter :: Bracket ) ,
102
- Delimiter :: None => quote !( crate :: Delimiter :: None ) ,
106
+ Delimiter :: Parenthesis => minimal_quote !( crate :: Delimiter :: Parenthesis ) ,
107
+ Delimiter :: Brace => minimal_quote !( crate :: Delimiter :: Brace ) ,
108
+ Delimiter :: Bracket => minimal_quote !( crate :: Delimiter :: Bracket ) ,
109
+ Delimiter :: None => minimal_quote !( crate :: Delimiter :: None ) ,
103
110
} ) ,
104
111
( @ quote( tt. stream( ) ) ) ,
105
- ) ) ) ,
106
- TokenTree :: Ident ( tt) => quote!( crate :: TokenTree :: Ident ( crate :: Ident :: new(
107
- ( @ TokenTree :: from( Literal :: string( & tt. to_string( ) ) ) ) ,
112
+ ) ) , & mut ts) ; )
113
+ }
114
+ TokenTree :: Ident ( tt) => {
115
+ let literal = tt. to_string ( ) ;
116
+ let ( literal, ctor) = if let Some ( stripped) = literal. strip_prefix ( "r#" ) {
117
+ ( stripped, minimal_quote ! ( crate :: Ident :: new_raw) )
118
+ } else {
119
+ ( literal. as_str ( ) , minimal_quote ! ( crate :: Ident :: new) )
120
+ } ;
121
+ minimal_quote ! ( crate :: ToTokens :: to_tokens( & crate :: TokenTree :: Ident ( ( @ ctor) (
122
+ ( @ TokenTree :: from( Literal :: string( literal) ) ) ,
108
123
( @ quote_span( proc_macro_crate. clone( ) , tt. span( ) ) ) ,
109
- ) ) ) ,
110
- TokenTree :: Literal ( tt) => quote!( crate :: TokenTree :: Literal ( {
124
+ ) ) , & mut ts) ; )
125
+ }
126
+ TokenTree :: Literal ( tt) => {
127
+ minimal_quote ! ( crate :: ToTokens :: to_tokens( & crate :: TokenTree :: Literal ( {
111
128
let mut iter = ( @ TokenTree :: from( Literal :: string( & tt. to_string( ) ) ) )
112
129
. parse:: <crate :: TokenStream >( )
113
130
. unwrap( )
@@ -120,22 +137,28 @@ pub fn quote(stream: TokenStream) -> TokenStream {
120
137
} else {
121
138
unreachable!( )
122
139
}
123
- } ) )
124
- } ) ) , ) )
125
- } )
126
- . collect :: < TokenStream > ( ) ;
127
-
140
+ } ) , & mut ts ) ; )
141
+ }
142
+ }
143
+ . to_tokens ( & mut tokens ) ;
144
+ }
128
145
if after_dollar {
129
146
panic ! ( "unexpected trailing `$` in `quote!`" ) ;
130
147
}
131
148
132
- quote ! ( [ ( @ tokens) ] . iter( ) . cloned( ) . collect:: <crate :: TokenStream >( ) )
149
+ minimal_quote ! {
150
+ {
151
+ let mut ts = crate :: TokenStream :: new( ) ;
152
+ ( @ tokens)
153
+ ts
154
+ }
155
+ }
133
156
}
134
157
135
158
/// Quote a `Span` into a `TokenStream`.
136
159
/// This is needed to implement a custom quoter.
137
160
#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
138
161
pub fn quote_span ( proc_macro_crate : TokenStream , span : Span ) -> TokenStream {
139
162
let id = span. save_span ( ) ;
140
- quote ! ( ( @ proc_macro_crate ) :: Span :: recover_proc_macro_span( ( @ TokenTree :: from( Literal :: usize_unsuffixed( id) ) ) ) )
163
+ minimal_quote ! ( ( @ proc_macro_crate ) :: Span :: recover_proc_macro_span( ( @ TokenTree :: from( Literal :: usize_unsuffixed( id) ) ) ) )
141
164
}
0 commit comments