44//! This quasiquoter uses macros 2.0 hygiene to reliably access
55//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
66
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+ } ;
810
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) * ) ) } ;
1315 ( , ) => { Punct :: new( ',' , Spacing :: Alone ) } ;
1416 ( . ) => { Punct :: new( '.' , Spacing :: Alone ) } ;
1517 ( ; ) => { Punct :: new( ';' , Spacing :: Alone ) } ;
@@ -21,21 +23,20 @@ macro_rules! quote_tt {
2123 ( $i: ident) => { Ident :: new( stringify!( $i) , Span :: def_site( ) ) } ;
2224}
2325
24- macro_rules! quote_ts {
26+ macro_rules! minimal_quote_ts {
2527 ( ( @ $( $t: tt) * ) ) => { $( $t) * } ;
2628 ( :: ) => {
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+ }
3738 } ;
38- ( $t: tt) => { TokenTree :: from( quote_tt !( $t) ) } ;
39+ ( $t: tt) => { TokenTree :: from( minimal_quote_tt !( $t) ) } ;
3940}
4041
4142/// Simpler version of the real `quote!` macro, implemented solely
@@ -46,12 +47,14 @@ macro_rules! quote_ts {
4647///
4748/// Note: supported tokens are a subset of the real `quote!`, but
4849/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
49- macro_rules! quote {
50- ( ) => { TokenStream :: new( ) } ;
50+ macro_rules! minimal_quote {
5151 ( $( $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+ }
5558 } ;
5659}
5760
@@ -62,52 +65,66 @@ macro_rules! quote {
6265#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
6366pub fn quote ( stream : TokenStream ) -> TokenStream {
6467 if stream. is_empty ( ) {
65- return quote ! ( crate :: TokenStream :: new( ) ) ;
68+ return minimal_quote ! ( crate :: TokenStream :: new( ) ) ;
6669 }
67- let proc_macro_crate = quote ! ( crate ) ;
70+ let proc_macro_crate = minimal_quote ! ( crate ) ;
6871 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 ;
8682 }
83+ TokenTree :: Punct ( ref tt) if tt. as_char ( ) == '$' => { }
84+ _ => panic ! ( "`$` must be followed by an ident or `$` in `quote!`" ) ,
8785 }
86+ } else if let TokenTree :: Punct ( ref tt) = tree {
87+ if tt. as_char ( ) == '$' {
88+ after_dollar = true ;
89+ continue ;
90+ }
91+ }
8892
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(
9196 ( @ TokenTree :: from( Literal :: character( tt. as_char( ) ) ) ) ,
9297 ( @ 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 ) ,
95100 } ) ,
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(
98105 ( @ 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 ) ,
103110 } ) ,
104111 ( @ 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) ) ) ,
108123 ( @ 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 ( {
111128 let mut iter = ( @ TokenTree :: from( Literal :: string( & tt. to_string( ) ) ) )
112129 . parse:: <crate :: TokenStream >( )
113130 . unwrap( )
@@ -120,22 +137,28 @@ pub fn quote(stream: TokenStream) -> TokenStream {
120137 } else {
121138 unreachable!( )
122139 }
123- } ) )
124- } ) ) , ) )
125- } )
126- . collect :: < TokenStream > ( ) ;
127-
140+ } ) , & mut ts ) ; )
141+ }
142+ }
143+ . to_tokens ( & mut tokens ) ;
144+ }
128145 if after_dollar {
129146 panic ! ( "unexpected trailing `$` in `quote!`" ) ;
130147 }
131148
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+ }
133156}
134157
135158/// Quote a `Span` into a `TokenStream`.
136159/// This is needed to implement a custom quoter.
137160#[ unstable( feature = "proc_macro_quote" , issue = "54722" ) ]
138161pub fn quote_span ( proc_macro_crate : TokenStream , span : Span ) -> TokenStream {
139162 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) ) ) ) )
141164}
0 commit comments