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