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