]> git.lizzy.rs Git - rust.git/blob - crates/hir_expand/src/quote.rs
internal: Split unresolve proc-macro error out of mbe
[rust.git] / crates / hir_expand / src / quote.rs
1 //! A simplified version of quote-crate like quasi quote macro
2
3 // A helper macro quote macro
4 // FIXME:
5 // 1. Not all puncts are handled
6 // 2. #()* pattern repetition not supported now
7 //    * But we can do it manually, see `test_quote_derive_copy_hack`
8 #[doc(hidden)]
9 #[macro_export]
10 macro_rules! __quote {
11     () => {
12         Vec::<tt::TokenTree>::new()
13     };
14
15     ( @SUBTREE $delim:ident $($tt:tt)* ) => {
16         {
17             let children = $crate::__quote!($($tt)*);
18             tt::Subtree {
19                 delimiter: Some(tt::Delimiter {
20                     kind: tt::DelimiterKind::$delim,
21                     id: tt::TokenId::unspecified(),
22                 }),
23                 token_trees: $crate::quote::IntoTt::to_tokens(children),
24             }
25         }
26     };
27
28     ( @PUNCT $first:literal ) => {
29         {
30             vec![
31                 tt::Leaf::Punct(tt::Punct {
32                     char: $first,
33                     spacing: tt::Spacing::Alone,
34                     id: tt::TokenId::unspecified(),
35                 }).into()
36             ]
37         }
38     };
39
40     ( @PUNCT $first:literal, $sec:literal ) => {
41         {
42             vec![
43                 tt::Leaf::Punct(tt::Punct {
44                     char: $first,
45                     spacing: tt::Spacing::Joint,
46                     id: tt::TokenId::unspecified(),
47                 }).into(),
48                 tt::Leaf::Punct(tt::Punct {
49                     char: $sec,
50                     spacing: tt::Spacing::Alone,
51                     id: tt::TokenId::unspecified(),
52                 }).into()
53             ]
54         }
55     };
56
57     // hash variable
58     ( # $first:ident $($tail:tt)* ) => {
59         {
60             let token = $crate::quote::ToTokenTree::to_token($first);
61             let mut tokens = vec![token.into()];
62             let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
63             tokens.append(&mut tail_tokens);
64             tokens
65         }
66     };
67
68     ( ## $first:ident $($tail:tt)* ) => {
69         {
70             let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>();
71             let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
72             tokens.append(&mut tail_tokens);
73             tokens
74         }
75     };
76
77     // Brace
78     ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
79     // Bracket
80     ( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
81     // Parenthesis
82     ( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
83
84     // Literal
85     ( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
86     // Ident
87     ( $tt:ident ) => {
88         vec![ {
89             tt::Leaf::Ident(tt::Ident {
90                 text: stringify!($tt).into(),
91                 id: tt::TokenId::unspecified(),
92             }).into()
93         }]
94     };
95
96     // Puncts
97     // FIXME: Not all puncts are handled
98     ( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
99     ( & ) => {$crate::__quote!(@PUNCT '&')};
100     ( , ) => {$crate::__quote!(@PUNCT ',')};
101     ( : ) => {$crate::__quote!(@PUNCT ':')};
102     ( ; ) => {$crate::__quote!(@PUNCT ';')};
103     ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
104     ( . ) => {$crate::__quote!(@PUNCT '.')};
105     ( < ) => {$crate::__quote!(@PUNCT '<')};
106     ( > ) => {$crate::__quote!(@PUNCT '>')};
107     ( ! ) => {$crate::__quote!(@PUNCT '!')};
108
109     ( $first:tt $($tail:tt)+ ) => {
110         {
111             let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
112             let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
113
114             tokens.append(&mut tail_tokens);
115             tokens
116         }
117     };
118 }
119
120 /// FIXME:
121 /// It probably should implement in proc-macro
122 #[macro_export]
123 macro_rules! quote {
124     ( $($tt:tt)* ) => {
125         $crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
126     }
127 }
128
129 pub(crate) trait IntoTt {
130     fn to_subtree(self) -> tt::Subtree;
131     fn to_tokens(self) -> Vec<tt::TokenTree>;
132 }
133
134 impl IntoTt for Vec<tt::TokenTree> {
135     fn to_subtree(self) -> tt::Subtree {
136         tt::Subtree { delimiter: None, token_trees: self }
137     }
138
139     fn to_tokens(self) -> Vec<tt::TokenTree> {
140         self
141     }
142 }
143
144 impl IntoTt for tt::Subtree {
145     fn to_subtree(self) -> tt::Subtree {
146         self
147     }
148
149     fn to_tokens(self) -> Vec<tt::TokenTree> {
150         vec![tt::TokenTree::Subtree(self)]
151     }
152 }
153
154 pub(crate) trait ToTokenTree {
155     fn to_token(self) -> tt::TokenTree;
156 }
157
158 impl ToTokenTree for tt::TokenTree {
159     fn to_token(self) -> tt::TokenTree {
160         self
161     }
162 }
163
164 impl ToTokenTree for tt::Subtree {
165     fn to_token(self) -> tt::TokenTree {
166         self.into()
167     }
168 }
169
170 macro_rules! impl_to_to_tokentrees {
171     ($($ty:ty => $this:ident $im:block);*) => {
172         $(
173             impl ToTokenTree for $ty {
174                 fn to_token($this) -> tt::TokenTree {
175                     let leaf: tt::Leaf = $im.into();
176                     leaf.into()
177                 }
178             }
179
180             impl ToTokenTree for &$ty {
181                 fn to_token($this) -> tt::TokenTree {
182                     let leaf: tt::Leaf = $im.clone().into();
183                     leaf.into()
184                 }
185             }
186         )*
187     }
188 }
189
190 impl_to_to_tokentrees! {
191     u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
192     usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193     i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
194     bool => self { tt::Ident{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
195     tt::Leaf => self { self };
196     tt::Literal => self { self };
197     tt::Ident => self { self };
198     tt::Punct => self { self };
199     &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}};
200     String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}
201 }
202
203 #[cfg(test)]
204 mod tests {
205     #[test]
206     fn test_quote_delimiters() {
207         assert_eq!(quote!({}).to_string(), "{}");
208         assert_eq!(quote!(()).to_string(), "()");
209         assert_eq!(quote!([]).to_string(), "[]");
210     }
211
212     #[test]
213     fn test_quote_idents() {
214         assert_eq!(quote!(32).to_string(), "32");
215         assert_eq!(quote!(struct).to_string(), "struct");
216     }
217
218     #[test]
219     fn test_quote_hash_simple_literal() {
220         let a = 20;
221         assert_eq!(quote!(#a).to_string(), "20");
222         let s: String = "hello".into();
223         assert_eq!(quote!(#s).to_string(), "\"hello\"");
224     }
225
226     fn mk_ident(name: &str) -> tt::Ident {
227         tt::Ident { text: name.into(), id: tt::TokenId::unspecified() }
228     }
229
230     #[test]
231     fn test_quote_hash_token_tree() {
232         let a = mk_ident("hello");
233
234         let quoted = quote!(#a);
235         assert_eq!(quoted.to_string(), "hello");
236         let t = format!("{:?}", quoted);
237         assert_eq!(t, "SUBTREE $\n  IDENT   hello 4294967295");
238     }
239
240     #[test]
241     fn test_quote_simple_derive_copy() {
242         let name = mk_ident("Foo");
243
244         let quoted = quote! {
245             impl Clone for #name {
246                 fn clone(&self) -> Self {
247                     Self {}
248                 }
249             }
250         };
251
252         assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
253     }
254
255     #[test]
256     fn test_quote_derive_copy_hack() {
257         // Assume the given struct is:
258         // struct Foo {
259         //  name: String,
260         //  id: u32,
261         // }
262         let struct_name = mk_ident("Foo");
263         let fields = [mk_ident("name"), mk_ident("id")];
264         let fields = fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees).flatten();
265
266         let list = tt::Subtree {
267             delimiter: Some(tt::Delimiter {
268                 kind: tt::DelimiterKind::Brace,
269                 id: tt::TokenId::unspecified(),
270             }),
271             token_trees: fields.collect(),
272         };
273
274         let quoted = quote! {
275             impl Clone for #struct_name {
276                 fn clone(&self) -> Self {
277                     Self #list
278                 }
279             }
280         };
281
282         assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
283     }
284 }