1 | #![allow ( |
2 | clippy::disallowed_names, |
3 | clippy::let_underscore_untyped, |
4 | clippy::shadow_unrelated, |
5 | clippy::unseparated_literal_suffix, |
6 | clippy::used_underscore_binding |
7 | )] |
8 | |
9 | extern crate proc_macro; |
10 | |
11 | use std::borrow::Cow; |
12 | use std::collections::BTreeSet; |
13 | |
14 | use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream}; |
15 | use quote::{format_ident, quote, quote_spanned, TokenStreamExt}; |
16 | |
17 | struct X; |
18 | |
19 | impl quote::ToTokens for X { |
20 | fn to_tokens(&self, tokens: &mut TokenStream) { |
21 | tokens.append(Ident::new("X" , Span::call_site())); |
22 | } |
23 | } |
24 | |
25 | #[test] |
26 | fn test_quote_impl() { |
27 | let tokens = quote! { |
28 | impl<'a, T: ToTokens> ToTokens for &'a T { |
29 | fn to_tokens(&self, tokens: &mut TokenStream) { |
30 | (**self).to_tokens(tokens) |
31 | } |
32 | } |
33 | }; |
34 | |
35 | let expected = concat!( |
36 | "impl < 'a , T : ToTokens > ToTokens for & 'a T { " , |
37 | "fn to_tokens (& self , tokens : & mut TokenStream) { " , |
38 | "(* * self) . to_tokens (tokens) " , |
39 | "} " , |
40 | "}" |
41 | ); |
42 | |
43 | assert_eq!(expected, tokens.to_string()); |
44 | } |
45 | |
46 | #[test] |
47 | fn test_quote_spanned_impl() { |
48 | let span = Span::call_site(); |
49 | let tokens = quote_spanned! {span=> |
50 | impl<'a, T: ToTokens> ToTokens for &'a T { |
51 | fn to_tokens(&self, tokens: &mut TokenStream) { |
52 | (**self).to_tokens(tokens) |
53 | } |
54 | } |
55 | }; |
56 | |
57 | let expected = concat!( |
58 | "impl < 'a , T : ToTokens > ToTokens for & 'a T { " , |
59 | "fn to_tokens (& self , tokens : & mut TokenStream) { " , |
60 | "(* * self) . to_tokens (tokens) " , |
61 | "} " , |
62 | "}" |
63 | ); |
64 | |
65 | assert_eq!(expected, tokens.to_string()); |
66 | } |
67 | |
68 | #[test] |
69 | fn test_substitution() { |
70 | let x = X; |
71 | let tokens = quote!(#x <#x> (#x) [#x] {#x}); |
72 | |
73 | let expected = "X < X > (X) [X] { X }" ; |
74 | |
75 | assert_eq!(expected, tokens.to_string()); |
76 | } |
77 | |
78 | #[test] |
79 | fn test_iter() { |
80 | let primes = &[X, X, X, X]; |
81 | |
82 | assert_eq!("X X X X" , quote!(#(#primes)*).to_string()); |
83 | |
84 | assert_eq!("X , X , X , X ," , quote!(#(#primes,)*).to_string()); |
85 | |
86 | assert_eq!("X , X , X , X" , quote!(#(#primes),*).to_string()); |
87 | } |
88 | |
89 | #[test] |
90 | fn test_array() { |
91 | let array: [u8; 40] = [0; 40]; |
92 | let _ = quote!(#(#array #array)*); |
93 | |
94 | let ref_array: &[u8; 40] = &[0; 40]; |
95 | let _ = quote!(#(#ref_array #ref_array)*); |
96 | |
97 | let ref_slice: &[u8] = &[0; 40]; |
98 | let _ = quote!(#(#ref_slice #ref_slice)*); |
99 | |
100 | let array: [X; 2] = [X, X]; // !Copy |
101 | let _ = quote!(#(#array #array)*); |
102 | |
103 | let ref_array: &[X; 2] = &[X, X]; |
104 | let _ = quote!(#(#ref_array #ref_array)*); |
105 | |
106 | let ref_slice: &[X] = &[X, X]; |
107 | let _ = quote!(#(#ref_slice #ref_slice)*); |
108 | } |
109 | |
110 | #[test] |
111 | fn test_advanced() { |
112 | let generics = quote!( <'a, T> ); |
113 | |
114 | let where_clause = quote!( where T: Serialize ); |
115 | |
116 | let field_ty = quote!(String); |
117 | |
118 | let item_ty = quote!(Cow<'a, str>); |
119 | |
120 | let path = quote!(SomeTrait::serialize_with); |
121 | |
122 | let value = quote!(self.x); |
123 | |
124 | let tokens = quote! { |
125 | struct SerializeWith #generics #where_clause { |
126 | value: &'a #field_ty, |
127 | phantom: ::std::marker::PhantomData<#item_ty>, |
128 | } |
129 | |
130 | impl #generics ::serde::Serialize for SerializeWith #generics #where_clause { |
131 | fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error> |
132 | where S: ::serde::Serializer |
133 | { |
134 | #path(self.value, s) |
135 | } |
136 | } |
137 | |
138 | SerializeWith { |
139 | value: #value, |
140 | phantom: ::std::marker::PhantomData::<#item_ty>, |
141 | } |
142 | }; |
143 | |
144 | let expected = concat!( |
145 | "struct SerializeWith < 'a , T > where T : Serialize { " , |
146 | "value : & 'a String , " , |
147 | "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , " , |
148 | "} " , |
149 | "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { " , |
150 | "fn serialize < S > (& self , s : & mut S) -> Result < () , S :: Error > " , |
151 | "where S : :: serde :: Serializer " , |
152 | "{ " , |
153 | "SomeTrait :: serialize_with (self . value , s) " , |
154 | "} " , |
155 | "} " , |
156 | "SerializeWith { " , |
157 | "value : self . x , " , |
158 | "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , " , |
159 | "}" |
160 | ); |
161 | |
162 | assert_eq!(expected, tokens.to_string()); |
163 | } |
164 | |
165 | #[test] |
166 | fn test_integer() { |
167 | let ii8 = -1i8; |
168 | let ii16 = -1i16; |
169 | let ii32 = -1i32; |
170 | let ii64 = -1i64; |
171 | let ii128 = -1i128; |
172 | let iisize = -1isize; |
173 | let uu8 = 1u8; |
174 | let uu16 = 1u16; |
175 | let uu32 = 1u32; |
176 | let uu64 = 1u64; |
177 | let uu128 = 1u128; |
178 | let uusize = 1usize; |
179 | |
180 | let tokens = quote! { |
181 | 1 1i32 1u256 |
182 | #ii8 #ii16 #ii32 #ii64 #ii128 #iisize |
183 | #uu8 #uu16 #uu32 #uu64 #uu128 #uusize |
184 | }; |
185 | let expected = |
186 | "1 1i32 1u256 - 1i8 - 1i16 - 1i32 - 1i64 - 1i128 - 1isize 1u8 1u16 1u32 1u64 1u128 1usize" ; |
187 | assert_eq!(expected, tokens.to_string()); |
188 | } |
189 | |
190 | #[test] |
191 | fn test_floating() { |
192 | let e32 = 2.345f32; |
193 | |
194 | let e64 = 2.345f64; |
195 | |
196 | let tokens = quote! { |
197 | #e32 |
198 | #e64 |
199 | }; |
200 | let expected = concat!("2.345f32 2.345f64" ); |
201 | assert_eq!(expected, tokens.to_string()); |
202 | } |
203 | |
204 | #[test] |
205 | fn test_char() { |
206 | let zero = ' \u{1}' ; |
207 | let pound = '#' ; |
208 | let quote = '"' ; |
209 | let apost = ' \'' ; |
210 | let newline = ' \n' ; |
211 | let heart = ' \u{2764}' ; |
212 | |
213 | let tokens = quote! { |
214 | #zero #pound #quote #apost #newline #heart |
215 | }; |
216 | let expected = "' \\u{1}' '#' ' \"' ' \\'' ' \\n' ' \u{2764}'" ; |
217 | assert_eq!(expected, tokens.to_string()); |
218 | } |
219 | |
220 | #[test] |
221 | fn test_str() { |
222 | let s = " \u{1} a 'b \" c" ; |
223 | let tokens = quote!(#s); |
224 | let expected = " \"\\u{1} a 'b \\\" c \"" ; |
225 | assert_eq!(expected, tokens.to_string()); |
226 | } |
227 | |
228 | #[test] |
229 | fn test_string() { |
230 | let s = " \u{1} a 'b \" c" .to_string(); |
231 | let tokens = quote!(#s); |
232 | let expected = " \"\\u{1} a 'b \\\" c \"" ; |
233 | assert_eq!(expected, tokens.to_string()); |
234 | } |
235 | |
236 | #[test] |
237 | fn test_interpolated_literal() { |
238 | macro_rules! m { |
239 | ($literal:literal) => { |
240 | quote!($literal) |
241 | }; |
242 | } |
243 | |
244 | let tokens = m!(1); |
245 | let expected = "1" ; |
246 | assert_eq!(expected, tokens.to_string()); |
247 | |
248 | let tokens = m!(-1); |
249 | let expected = "- 1" ; |
250 | assert_eq!(expected, tokens.to_string()); |
251 | |
252 | let tokens = m!(true); |
253 | let expected = "true" ; |
254 | assert_eq!(expected, tokens.to_string()); |
255 | |
256 | let tokens = m!(-true); |
257 | let expected = "- true" ; |
258 | assert_eq!(expected, tokens.to_string()); |
259 | } |
260 | |
261 | #[test] |
262 | fn test_ident() { |
263 | let foo = Ident::new("Foo" , Span::call_site()); |
264 | let bar = Ident::new(&format!("Bar{}" , 7), Span::call_site()); |
265 | let tokens = quote!(struct #foo; enum #bar {}); |
266 | let expected = "struct Foo ; enum Bar7 { }" ; |
267 | assert_eq!(expected, tokens.to_string()); |
268 | } |
269 | |
270 | #[test] |
271 | fn test_underscore() { |
272 | let tokens = quote!(let _;); |
273 | let expected = "let _ ;" ; |
274 | assert_eq!(expected, tokens.to_string()); |
275 | } |
276 | |
277 | #[test] |
278 | fn test_duplicate() { |
279 | let ch = 'x' ; |
280 | |
281 | let tokens = quote!(#ch #ch); |
282 | |
283 | let expected = "'x' 'x'" ; |
284 | assert_eq!(expected, tokens.to_string()); |
285 | } |
286 | |
287 | #[test] |
288 | fn test_fancy_repetition() { |
289 | let foo = vec!["a" , "b" ]; |
290 | let bar = vec![true, false]; |
291 | |
292 | let tokens = quote! { |
293 | #(#foo: #bar),* |
294 | }; |
295 | |
296 | let expected = r#""a" : true , "b" : false"# ; |
297 | assert_eq!(expected, tokens.to_string()); |
298 | } |
299 | |
300 | #[test] |
301 | fn test_nested_fancy_repetition() { |
302 | let nested = vec![vec!['a' , 'b' , 'c' ], vec!['x' , 'y' , 'z' ]]; |
303 | |
304 | let tokens = quote! { |
305 | #( |
306 | #(#nested)* |
307 | ),* |
308 | }; |
309 | |
310 | let expected = "'a' 'b' 'c' , 'x' 'y' 'z'" ; |
311 | assert_eq!(expected, tokens.to_string()); |
312 | } |
313 | |
314 | #[test] |
315 | fn test_duplicate_name_repetition() { |
316 | let foo = &["a" , "b" ]; |
317 | |
318 | let tokens = quote! { |
319 | #(#foo: #foo),* |
320 | #(#foo: #foo),* |
321 | }; |
322 | |
323 | let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""# ; |
324 | assert_eq!(expected, tokens.to_string()); |
325 | } |
326 | |
327 | #[test] |
328 | fn test_duplicate_name_repetition_no_copy() { |
329 | let foo = vec!["a" .to_owned(), "b" .to_owned()]; |
330 | |
331 | let tokens = quote! { |
332 | #(#foo: #foo),* |
333 | }; |
334 | |
335 | let expected = r#""a" : "a" , "b" : "b""# ; |
336 | assert_eq!(expected, tokens.to_string()); |
337 | } |
338 | |
339 | #[test] |
340 | fn test_btreeset_repetition() { |
341 | let mut set = BTreeSet::new(); |
342 | set.insert("a" .to_owned()); |
343 | set.insert("b" .to_owned()); |
344 | |
345 | let tokens = quote! { |
346 | #(#set: #set),* |
347 | }; |
348 | |
349 | let expected = r#""a" : "a" , "b" : "b""# ; |
350 | assert_eq!(expected, tokens.to_string()); |
351 | } |
352 | |
353 | #[test] |
354 | fn test_variable_name_conflict() { |
355 | // The implementation of `#(...),*` uses the variable `_i` but it should be |
356 | // fine, if a little confusing when debugging. |
357 | let _i = vec!['a' , 'b' ]; |
358 | let tokens = quote! { #(#_i),* }; |
359 | let expected = "'a' , 'b'" ; |
360 | assert_eq!(expected, tokens.to_string()); |
361 | } |
362 | |
363 | #[test] |
364 | fn test_nonrep_in_repetition() { |
365 | let rep = vec!["a" , "b" ]; |
366 | let nonrep = "c" ; |
367 | |
368 | let tokens = quote! { |
369 | #(#rep #rep : #nonrep #nonrep),* |
370 | }; |
371 | |
372 | let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""# ; |
373 | assert_eq!(expected, tokens.to_string()); |
374 | } |
375 | |
376 | #[test] |
377 | fn test_empty_quote() { |
378 | let tokens = quote!(); |
379 | assert_eq!("" , tokens.to_string()); |
380 | } |
381 | |
382 | #[test] |
383 | fn test_box_str() { |
384 | let b = "str" .to_owned().into_boxed_str(); |
385 | let tokens = quote! { #b }; |
386 | assert_eq!(" \"str \"" , tokens.to_string()); |
387 | } |
388 | |
389 | #[test] |
390 | fn test_cow() { |
391 | let owned: Cow<Ident> = Cow::Owned(Ident::new("owned" , Span::call_site())); |
392 | |
393 | let ident = Ident::new("borrowed" , Span::call_site()); |
394 | let borrowed = Cow::Borrowed(&ident); |
395 | |
396 | let tokens = quote! { #owned #borrowed }; |
397 | assert_eq!("owned borrowed" , tokens.to_string()); |
398 | } |
399 | |
400 | #[test] |
401 | fn test_closure() { |
402 | fn field_i(i: usize) -> Ident { |
403 | format_ident!("__field{}" , i) |
404 | } |
405 | |
406 | let fields = (0usize..3) |
407 | .map(field_i as fn(_) -> _) |
408 | .map(|var| quote! { #var }); |
409 | |
410 | let tokens = quote! { #(#fields)* }; |
411 | assert_eq!("__field0 __field1 __field2" , tokens.to_string()); |
412 | } |
413 | |
414 | #[test] |
415 | fn test_append_tokens() { |
416 | let mut a = quote!(a); |
417 | let b = quote!(b); |
418 | a.append_all(b); |
419 | assert_eq!("a b" , a.to_string()); |
420 | } |
421 | |
422 | #[test] |
423 | fn test_format_ident() { |
424 | let id0 = format_ident!("Aa" ); |
425 | let id1 = format_ident!("Hello{x}" , x = id0); |
426 | let id2 = format_ident!("Hello{x}" , x = 5usize); |
427 | let id3 = format_ident!("Hello{}_{x}" , id0, x = 10usize); |
428 | let id4 = format_ident!("Aa" , span = Span::call_site()); |
429 | let id5 = format_ident!("Hello{}" , Cow::Borrowed("World" )); |
430 | |
431 | assert_eq!(id0, "Aa" ); |
432 | assert_eq!(id1, "HelloAa" ); |
433 | assert_eq!(id2, "Hello5" ); |
434 | assert_eq!(id3, "HelloAa_10" ); |
435 | assert_eq!(id4, "Aa" ); |
436 | assert_eq!(id5, "HelloWorld" ); |
437 | } |
438 | |
439 | #[test] |
440 | fn test_format_ident_strip_raw() { |
441 | let id = format_ident!("r#struct" ); |
442 | let my_id = format_ident!("MyId{}" , id); |
443 | let raw_my_id = format_ident!("r#MyId{}" , id); |
444 | |
445 | assert_eq!(id, "r#struct" ); |
446 | assert_eq!(my_id, "MyIdstruct" ); |
447 | assert_eq!(raw_my_id, "r#MyIdstruct" ); |
448 | } |
449 | |
450 | #[test] |
451 | fn test_outer_line_comment() { |
452 | let tokens = quote! { |
453 | /// doc |
454 | }; |
455 | let expected = "# [doc = r \" doc \"]" ; |
456 | assert_eq!(expected, tokens.to_string()); |
457 | } |
458 | |
459 | #[test] |
460 | fn test_inner_line_comment() { |
461 | let tokens = quote! { |
462 | //! doc |
463 | }; |
464 | let expected = "# ! [doc = r \" doc \"]" ; |
465 | assert_eq!(expected, tokens.to_string()); |
466 | } |
467 | |
468 | #[test] |
469 | fn test_outer_block_comment() { |
470 | let tokens = quote! { |
471 | /** doc */ |
472 | }; |
473 | let expected = "# [doc = r \" doc \"]" ; |
474 | assert_eq!(expected, tokens.to_string()); |
475 | } |
476 | |
477 | #[test] |
478 | fn test_inner_block_comment() { |
479 | let tokens = quote! { |
480 | /*! doc */ |
481 | }; |
482 | let expected = "# ! [doc = r \" doc \"]" ; |
483 | assert_eq!(expected, tokens.to_string()); |
484 | } |
485 | |
486 | #[test] |
487 | fn test_outer_attr() { |
488 | let tokens = quote! { |
489 | #[inline] |
490 | }; |
491 | let expected = "# [inline]" ; |
492 | assert_eq!(expected, tokens.to_string()); |
493 | } |
494 | |
495 | #[test] |
496 | fn test_inner_attr() { |
497 | let tokens = quote! { |
498 | #![no_std] |
499 | }; |
500 | let expected = "# ! [no_std]" ; |
501 | assert_eq!(expected, tokens.to_string()); |
502 | } |
503 | |
504 | // https://github.com/dtolnay/quote/issues/130 |
505 | #[test] |
506 | fn test_star_after_repetition() { |
507 | let c = vec!['0' , '1' ]; |
508 | let tokens = quote! { |
509 | #( |
510 | f(#c); |
511 | )* |
512 | *out = None; |
513 | }; |
514 | let expected = "f ('0') ; f ('1') ; * out = None ;" ; |
515 | assert_eq!(expected, tokens.to_string()); |
516 | } |
517 | |
518 | #[test] |
519 | fn test_quote_raw_id() { |
520 | let id = quote!(r#raw_id); |
521 | assert_eq!(id.to_string(), "r#raw_id" ); |
522 | } |
523 | |
524 | #[test] |
525 | fn test_type_inference_for_span() { |
526 | trait CallSite { |
527 | fn get() -> Self; |
528 | } |
529 | |
530 | impl CallSite for Span { |
531 | fn get() -> Self { |
532 | Span::call_site() |
533 | } |
534 | } |
535 | |
536 | let span = Span::call_site(); |
537 | let _ = quote_spanned!(span=> ...); |
538 | |
539 | let delim_span = Group::new(Delimiter::Parenthesis, TokenStream::new()).delim_span(); |
540 | let _ = quote_spanned!(delim_span=> ...); |
541 | |
542 | let inferred = CallSite::get(); |
543 | let _ = quote_spanned!(inferred=> ...); |
544 | |
545 | if false { |
546 | let proc_macro_span = proc_macro::Span::call_site(); |
547 | let _ = quote_spanned!(proc_macro_span.into()=> ...); |
548 | } |
549 | } |
550 | |