1#[macro_use]
2mod macros;
3
4use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
5use quote::quote;
6use std::iter::FromIterator;
7use syn::Type;
8
9#[test]
10fn 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]
20fn 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]
94fn 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]
146fn 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]
223fn 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]
291fn 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