1 #![allow(clippy::uninlined_format_args)]
2 
3 #[macro_use]
4 mod macros;
5 
6 use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
7 use quote::{quote, ToTokens as _};
8 use syn::punctuated::Punctuated;
9 use syn::{parse_quote, token, Token, Type, TypeTuple};
10 
11 #[test]
test_mut_self()12 fn test_mut_self() {
13     syn::parse_str::<Type>("fn(mut self)").unwrap();
14     syn::parse_str::<Type>("fn(mut self,)").unwrap();
15     syn::parse_str::<Type>("fn(mut self: ())").unwrap();
16     syn::parse_str::<Type>("fn(mut self: ...)").unwrap_err();
17     syn::parse_str::<Type>("fn(mut self: mut self)").unwrap_err();
18     syn::parse_str::<Type>("fn(mut self::T)").unwrap_err();
19 }
20 
21 #[test]
test_macro_variable_type()22 fn test_macro_variable_type() {
23     // mimics the token stream corresponding to `$ty<T>`
24     let tokens = TokenStream::from_iter(vec![
25         TokenTree::Group(Group::new(Delimiter::None, quote! { ty })),
26         TokenTree::Punct(Punct::new('<', Spacing::Alone)),
27         TokenTree::Ident(Ident::new("T", Span::call_site())),
28         TokenTree::Punct(Punct::new('>', Spacing::Alone)),
29     ]);
30 
31     snapshot!(tokens as Type, @r###"
32     Type::Path {
33         path: Path {
34             segments: [
35                 PathSegment {
36                     ident: "ty",
37                     arguments: PathArguments::AngleBracketed {
38                         args: [
39                             GenericArgument::Type(Type::Path {
40                                 path: Path {
41                                     segments: [
42                                         PathSegment {
43                                             ident: "T",
44                                         },
45                                     ],
46                                 },
47                             }),
48                         ],
49                     },
50                 },
51             ],
52         },
53     }
54     "###);
55 
56     // mimics the token stream corresponding to `$ty::<T>`
57     let tokens = TokenStream::from_iter(vec![
58         TokenTree::Group(Group::new(Delimiter::None, quote! { ty })),
59         TokenTree::Punct(Punct::new(':', Spacing::Joint)),
60         TokenTree::Punct(Punct::new(':', Spacing::Alone)),
61         TokenTree::Punct(Punct::new('<', Spacing::Alone)),
62         TokenTree::Ident(Ident::new("T", Span::call_site())),
63         TokenTree::Punct(Punct::new('>', Spacing::Alone)),
64     ]);
65 
66     snapshot!(tokens as Type, @r###"
67     Type::Path {
68         path: Path {
69             segments: [
70                 PathSegment {
71                     ident: "ty",
72                     arguments: PathArguments::AngleBracketed {
73                         colon2_token: Some,
74                         args: [
75                             GenericArgument::Type(Type::Path {
76                                 path: Path {
77                                     segments: [
78                                         PathSegment {
79                                             ident: "T",
80                                         },
81                                     ],
82                                 },
83                             }),
84                         ],
85                     },
86                 },
87             ],
88         },
89     }
90     "###);
91 }
92 
93 #[test]
test_group_angle_brackets()94 fn test_group_angle_brackets() {
95     // mimics the token stream corresponding to `Option<$ty>`
96     let tokens = TokenStream::from_iter(vec![
97         TokenTree::Ident(Ident::new("Option", Span::call_site())),
98         TokenTree::Punct(Punct::new('<', Spacing::Alone)),
99         TokenTree::Group(Group::new(Delimiter::None, quote! { Vec<u8> })),
100         TokenTree::Punct(Punct::new('>', Spacing::Alone)),
101     ]);
102 
103     snapshot!(tokens as Type, @r###"
104     Type::Path {
105         path: Path {
106             segments: [
107                 PathSegment {
108                     ident: "Option",
109                     arguments: PathArguments::AngleBracketed {
110                         args: [
111                             GenericArgument::Type(Type::Group {
112                                 elem: Type::Path {
113                                     path: Path {
114                                         segments: [
115                                             PathSegment {
116                                                 ident: "Vec",
117                                                 arguments: PathArguments::AngleBracketed {
118                                                     args: [
119                                                         GenericArgument::Type(Type::Path {
120                                                             path: Path {
121                                                                 segments: [
122                                                                     PathSegment {
123                                                                         ident: "u8",
124                                                                     },
125                                                                 ],
126                                                             },
127                                                         }),
128                                                     ],
129                                                 },
130                                             },
131                                         ],
132                                     },
133                                 },
134                             }),
135                         ],
136                     },
137                 },
138             ],
139         },
140     }
141     "###);
142 }
143 
144 #[test]
test_group_colons()145 fn test_group_colons() {
146     // mimics the token stream corresponding to `$ty::Item`
147     let tokens = TokenStream::from_iter(vec![
148         TokenTree::Group(Group::new(Delimiter::None, quote! { Vec<u8> })),
149         TokenTree::Punct(Punct::new(':', Spacing::Joint)),
150         TokenTree::Punct(Punct::new(':', Spacing::Alone)),
151         TokenTree::Ident(Ident::new("Item", Span::call_site())),
152     ]);
153 
154     snapshot!(tokens as Type, @r###"
155     Type::Path {
156         path: Path {
157             segments: [
158                 PathSegment {
159                     ident: "Vec",
160                     arguments: PathArguments::AngleBracketed {
161                         args: [
162                             GenericArgument::Type(Type::Path {
163                                 path: Path {
164                                     segments: [
165                                         PathSegment {
166                                             ident: "u8",
167                                         },
168                                     ],
169                                 },
170                             }),
171                         ],
172                     },
173                 },
174                 Token![::],
175                 PathSegment {
176                     ident: "Item",
177                 },
178             ],
179         },
180     }
181     "###);
182 
183     let tokens = TokenStream::from_iter(vec![
184         TokenTree::Group(Group::new(Delimiter::None, quote! { [T] })),
185         TokenTree::Punct(Punct::new(':', Spacing::Joint)),
186         TokenTree::Punct(Punct::new(':', Spacing::Alone)),
187         TokenTree::Ident(Ident::new("Element", Span::call_site())),
188     ]);
189 
190     snapshot!(tokens as Type, @r###"
191     Type::Path {
192         qself: Some(QSelf {
193             ty: Type::Slice {
194                 elem: Type::Path {
195                     path: Path {
196                         segments: [
197                             PathSegment {
198                                 ident: "T",
199                             },
200                         ],
201                     },
202                 },
203             },
204             position: 0,
205         }),
206         path: Path {
207             leading_colon: Some,
208             segments: [
209                 PathSegment {
210                     ident: "Element",
211                 },
212             ],
213         },
214     }
215     "###);
216 }
217 
218 #[test]
test_trait_object()219 fn test_trait_object() {
220     let tokens = quote!(dyn for<'a> Trait<'a> + 'static);
221     snapshot!(tokens as Type, @r###"
222     Type::TraitObject {
223         dyn_token: Some,
224         bounds: [
225             TypeParamBound::Trait(TraitBound {
226                 lifetimes: Some(BoundLifetimes {
227                     lifetimes: [
228                         GenericParam::Lifetime(LifetimeParam {
229                             lifetime: Lifetime {
230                                 ident: "a",
231                             },
232                         }),
233                     ],
234                 }),
235                 path: Path {
236                     segments: [
237                         PathSegment {
238                             ident: "Trait",
239                             arguments: PathArguments::AngleBracketed {
240                                 args: [
241                                     GenericArgument::Lifetime(Lifetime {
242                                         ident: "a",
243                                     }),
244                                 ],
245                             },
246                         },
247                     ],
248                 },
249             }),
250             Token![+],
251             TypeParamBound::Lifetime {
252                 ident: "static",
253             },
254         ],
255     }
256     "###);
257 
258     let tokens = quote!(dyn 'a + Trait);
259     snapshot!(tokens as Type, @r###"
260     Type::TraitObject {
261         dyn_token: Some,
262         bounds: [
263             TypeParamBound::Lifetime {
264                 ident: "a",
265             },
266             Token![+],
267             TypeParamBound::Trait(TraitBound {
268                 path: Path {
269                     segments: [
270                         PathSegment {
271                             ident: "Trait",
272                         },
273                     ],
274                 },
275             }),
276         ],
277     }
278     "###);
279 
280     // None of the following are valid Rust types.
281     syn::parse_str::<Type>("for<'a> dyn Trait<'a>").unwrap_err();
282     syn::parse_str::<Type>("dyn for<'a> 'a + Trait").unwrap_err();
283 }
284 
285 #[test]
test_trailing_plus()286 fn test_trailing_plus() {
287     #[rustfmt::skip]
288     let tokens = quote!(impl Trait +);
289     snapshot!(tokens as Type, @r###"
290     Type::ImplTrait {
291         bounds: [
292             TypeParamBound::Trait(TraitBound {
293                 path: Path {
294                     segments: [
295                         PathSegment {
296                             ident: "Trait",
297                         },
298                     ],
299                 },
300             }),
301             Token![+],
302         ],
303     }
304     "###);
305 
306     #[rustfmt::skip]
307     let tokens = quote!(dyn Trait +);
308     snapshot!(tokens as Type, @r###"
309     Type::TraitObject {
310         dyn_token: Some,
311         bounds: [
312             TypeParamBound::Trait(TraitBound {
313                 path: Path {
314                     segments: [
315                         PathSegment {
316                             ident: "Trait",
317                         },
318                     ],
319                 },
320             }),
321             Token![+],
322         ],
323     }
324     "###);
325 
326     #[rustfmt::skip]
327     let tokens = quote!(Trait +);
328     snapshot!(tokens as Type, @r###"
329     Type::TraitObject {
330         bounds: [
331             TypeParamBound::Trait(TraitBound {
332                 path: Path {
333                     segments: [
334                         PathSegment {
335                             ident: "Trait",
336                         },
337                     ],
338                 },
339             }),
340             Token![+],
341         ],
342     }
343     "###);
344 }
345 
346 #[test]
test_tuple_comma()347 fn test_tuple_comma() {
348     let mut expr = TypeTuple {
349         paren_token: token::Paren::default(),
350         elems: Punctuated::new(),
351     };
352     snapshot!(expr.to_token_stream() as Type, @"Type::Tuple");
353 
354     expr.elems.push_value(parse_quote!(_));
355     // Must not parse to Type::Paren
356     snapshot!(expr.to_token_stream() as Type, @r###"
357     Type::Tuple {
358         elems: [
359             Type::Infer,
360             Token![,],
361         ],
362     }
363     "###);
364 
365     expr.elems.push_punct(<Token![,]>::default());
366     snapshot!(expr.to_token_stream() as Type, @r###"
367     Type::Tuple {
368         elems: [
369             Type::Infer,
370             Token![,],
371         ],
372     }
373     "###);
374 
375     expr.elems.push_value(parse_quote!(_));
376     snapshot!(expr.to_token_stream() as Type, @r###"
377     Type::Tuple {
378         elems: [
379             Type::Infer,
380             Token![,],
381             Type::Infer,
382         ],
383     }
384     "###);
385 
386     expr.elems.push_punct(<Token![,]>::default());
387     snapshot!(expr.to_token_stream() as Type, @r###"
388     Type::Tuple {
389         elems: [
390             Type::Infer,
391             Token![,],
392             Type::Infer,
393             Token![,],
394         ],
395     }
396     "###);
397 }
398