proc_macro/
quote.rs
1use crate::{
8 Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree,
9};
10
11macro_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)*)) };
15 (,) => { Punct::new(',', Spacing::Alone) };
16 (.) => { Punct::new('.', Spacing::Alone) };
17 (;) => { Punct::new(';', Spacing::Alone) };
18 (!) => { Punct::new('!', Spacing::Alone) };
19 (<) => { Punct::new('<', Spacing::Alone) };
20 (>) => { Punct::new('>', Spacing::Alone) };
21 (&) => { Punct::new('&', Spacing::Alone) };
22 (=) => { Punct::new('=', Spacing::Alone) };
23 ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
24}
25
26macro_rules! minimal_quote_ts {
27 ((@ $($t:tt)*)) => { $($t)* };
28 (::) => {
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 }
38 };
39 ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) };
40}
41
42macro_rules! minimal_quote {
51 ($($t:tt)*) => {
52 {
53 #[allow(unused_mut)] let mut ts = TokenStream::new();
55 $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)*
56 ts
57 }
58 };
59}
60
61#[unstable(feature = "proc_macro_quote", issue = "54722")]
66pub fn quote(stream: TokenStream) -> TokenStream {
67 if stream.is_empty() {
68 return minimal_quote!(crate::TokenStream::new());
69 }
70 let proc_macro_crate = minimal_quote!(crate);
71 let mut after_dollar = false;
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;
82 }
83 TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
84 _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
85 }
86 } else if let TokenTree::Punct(ref tt) = tree {
87 if tt.as_char() == '$' {
88 after_dollar = true;
89 continue;
90 }
91 }
92
93 match tree {
94 TokenTree::Punct(tt) => {
95 minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(
96 (@ TokenTree::from(Literal::character(tt.as_char()))),
97 (@ match tt.spacing() {
98 Spacing::Alone => minimal_quote!(crate::Spacing::Alone),
99 Spacing::Joint => minimal_quote!(crate::Spacing::Joint),
100 }),
101 )), &mut ts);)
102 }
103 TokenTree::Group(tt) => {
104 minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new(
105 (@ match tt.delimiter() {
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),
110 }),
111 (@ quote(tt.stream())),
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))),
123 (@ quote_span(proc_macro_crate.clone(), tt.span())),
124 )), &mut ts);)
125 }
126 TokenTree::Literal(tt) => {
127 minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({
128 let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
129 .parse::<crate::TokenStream>()
130 .unwrap()
131 .into_iter();
132 if let (Some(crate::TokenTree::Literal(mut lit)), None) =
133 (iter.next(), iter.next())
134 {
135 lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
136 lit
137 } else {
138 unreachable!()
139 }
140 }), &mut ts);)
141 }
142 }
143 .to_tokens(&mut tokens);
144 }
145 if after_dollar {
146 panic!("unexpected trailing `$` in `quote!`");
147 }
148
149 minimal_quote! {
150 {
151 let mut ts = crate::TokenStream::new();
152 (@ tokens)
153 ts
154 }
155 }
156}
157
158#[unstable(feature = "proc_macro_quote", issue = "54722")]
161pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
162 let id = span.save_span();
163 minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
164}