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