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