1#![allow(clippy::uninlined_format_args)]
2
3#[macro_use]
4mod macros;
5
6use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
7use quote::quote;
8use syn::Type;
9
10#[test]
11fn 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]
21fn 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]
93fn 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]
144fn 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]
217fn 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]
282fn 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