1 | #[macro_use ] |
2 | mod macros; |
3 | |
4 | use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; |
5 | use quote::quote; |
6 | use std::iter::FromIterator; |
7 | use syn::{Item, ItemTrait}; |
8 | |
9 | #[test] |
10 | fn test_macro_variable_attr() { |
11 | // mimics the token stream corresponding to `$attr fn f() {}` |
12 | let tokens = TokenStream::from_iter(vec![ |
13 | TokenTree::Group(Group::new(Delimiter::None, quote! { #[test] })), |
14 | TokenTree::Ident(Ident::new("fn" , Span::call_site())), |
15 | TokenTree::Ident(Ident::new("f" , Span::call_site())), |
16 | TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), |
17 | TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), |
18 | ]); |
19 | |
20 | snapshot!(tokens as Item, @r###" |
21 | Item::Fn { |
22 | attrs: [ |
23 | Attribute { |
24 | style: Outer, |
25 | path: Path { |
26 | segments: [ |
27 | PathSegment { |
28 | ident: "test", |
29 | arguments: None, |
30 | }, |
31 | ], |
32 | }, |
33 | tokens: TokenStream(``), |
34 | }, |
35 | ], |
36 | vis: Inherited, |
37 | sig: Signature { |
38 | ident: "f", |
39 | generics: Generics, |
40 | output: Default, |
41 | }, |
42 | block: Block, |
43 | } |
44 | "### ); |
45 | } |
46 | |
47 | #[test] |
48 | fn test_negative_impl() { |
49 | // Rustc parses all of the following. |
50 | |
51 | #[cfg (any())] |
52 | impl ! {} |
53 | let tokens = quote! { |
54 | impl ! {} |
55 | }; |
56 | snapshot!(tokens as Item, @r###" |
57 | Item::Impl { |
58 | generics: Generics, |
59 | self_ty: Type::Never, |
60 | } |
61 | "### ); |
62 | |
63 | #[cfg (any())] |
64 | #[rustfmt::skip] |
65 | impl !Trait {} |
66 | let tokens = quote! { |
67 | impl !Trait {} |
68 | }; |
69 | snapshot!(tokens as Item, @r###" |
70 | Item::Impl { |
71 | generics: Generics, |
72 | self_ty: Verbatim(`! Trait`), |
73 | } |
74 | "### ); |
75 | |
76 | #[cfg (any())] |
77 | impl !Trait for T {} |
78 | let tokens = quote! { |
79 | impl !Trait for T {} |
80 | }; |
81 | snapshot!(tokens as Item, @r###" |
82 | Item::Impl { |
83 | generics: Generics, |
84 | trait_: Some(( |
85 | Some, |
86 | Path { |
87 | segments: [ |
88 | PathSegment { |
89 | ident: "Trait", |
90 | arguments: None, |
91 | }, |
92 | ], |
93 | }, |
94 | )), |
95 | self_ty: Type::Path { |
96 | path: Path { |
97 | segments: [ |
98 | PathSegment { |
99 | ident: "T", |
100 | arguments: None, |
101 | }, |
102 | ], |
103 | }, |
104 | }, |
105 | } |
106 | "### ); |
107 | |
108 | #[cfg (any())] |
109 | #[rustfmt::skip] |
110 | impl !! {} |
111 | let tokens = quote! { |
112 | impl !! {} |
113 | }; |
114 | snapshot!(tokens as Item, @r###" |
115 | Item::Impl { |
116 | generics: Generics, |
117 | self_ty: Verbatim(`! !`), |
118 | } |
119 | "### ); |
120 | } |
121 | |
122 | #[test] |
123 | fn test_macro_variable_impl() { |
124 | // mimics the token stream corresponding to `impl $trait for $ty {}` |
125 | let tokens = TokenStream::from_iter(vec![ |
126 | TokenTree::Ident(Ident::new("impl" , Span::call_site())), |
127 | TokenTree::Group(Group::new(Delimiter::None, quote!(Trait))), |
128 | TokenTree::Ident(Ident::new("for" , Span::call_site())), |
129 | TokenTree::Group(Group::new(Delimiter::None, quote!(Type))), |
130 | TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), |
131 | ]); |
132 | |
133 | snapshot!(tokens as Item, @r###" |
134 | Item::Impl { |
135 | generics: Generics, |
136 | trait_: Some(( |
137 | None, |
138 | Path { |
139 | segments: [ |
140 | PathSegment { |
141 | ident: "Trait", |
142 | arguments: None, |
143 | }, |
144 | ], |
145 | }, |
146 | )), |
147 | self_ty: Type::Group { |
148 | elem: Type::Path { |
149 | path: Path { |
150 | segments: [ |
151 | PathSegment { |
152 | ident: "Type", |
153 | arguments: None, |
154 | }, |
155 | ], |
156 | }, |
157 | }, |
158 | }, |
159 | } |
160 | "### ); |
161 | } |
162 | |
163 | #[test] |
164 | fn test_supertraits() { |
165 | // Rustc parses all of the following. |
166 | |
167 | #[rustfmt::skip] |
168 | let tokens = quote!(trait Trait where {}); |
169 | snapshot!(tokens as ItemTrait, @r###" |
170 | ItemTrait { |
171 | vis: Inherited, |
172 | ident: "Trait", |
173 | generics: Generics { |
174 | where_clause: Some(WhereClause), |
175 | }, |
176 | } |
177 | "### ); |
178 | |
179 | #[rustfmt::skip] |
180 | let tokens = quote!(trait Trait: where {}); |
181 | snapshot!(tokens as ItemTrait, @r###" |
182 | ItemTrait { |
183 | vis: Inherited, |
184 | ident: "Trait", |
185 | generics: Generics { |
186 | where_clause: Some(WhereClause), |
187 | }, |
188 | colon_token: Some, |
189 | } |
190 | "### ); |
191 | |
192 | #[rustfmt::skip] |
193 | let tokens = quote!(trait Trait: Sized where {}); |
194 | snapshot!(tokens as ItemTrait, @r###" |
195 | ItemTrait { |
196 | vis: Inherited, |
197 | ident: "Trait", |
198 | generics: Generics { |
199 | where_clause: Some(WhereClause), |
200 | }, |
201 | colon_token: Some, |
202 | supertraits: [ |
203 | Trait(TraitBound { |
204 | modifier: None, |
205 | path: Path { |
206 | segments: [ |
207 | PathSegment { |
208 | ident: "Sized", |
209 | arguments: None, |
210 | }, |
211 | ], |
212 | }, |
213 | }), |
214 | ], |
215 | } |
216 | "### ); |
217 | |
218 | #[rustfmt::skip] |
219 | let tokens = quote!(trait Trait: Sized + where {}); |
220 | snapshot!(tokens as ItemTrait, @r###" |
221 | ItemTrait { |
222 | vis: Inherited, |
223 | ident: "Trait", |
224 | generics: Generics { |
225 | where_clause: Some(WhereClause), |
226 | }, |
227 | colon_token: Some, |
228 | supertraits: [ |
229 | Trait(TraitBound { |
230 | modifier: None, |
231 | path: Path { |
232 | segments: [ |
233 | PathSegment { |
234 | ident: "Sized", |
235 | arguments: None, |
236 | }, |
237 | ], |
238 | }, |
239 | }), |
240 | ], |
241 | } |
242 | "### ); |
243 | } |
244 | |
245 | #[test] |
246 | fn test_type_empty_bounds() { |
247 | #[rustfmt::skip] |
248 | let tokens = quote! { |
249 | trait Foo { |
250 | type Bar: ; |
251 | } |
252 | }; |
253 | |
254 | snapshot!(tokens as ItemTrait, @r###" |
255 | ItemTrait { |
256 | vis: Inherited, |
257 | ident: "Foo", |
258 | generics: Generics, |
259 | items: [ |
260 | TraitItem::Type { |
261 | ident: "Bar", |
262 | generics: Generics, |
263 | colon_token: Some, |
264 | }, |
265 | ], |
266 | } |
267 | "### ); |
268 | } |
269 | |
270 | #[test] |
271 | fn test_impl_visibility() { |
272 | let tokens = quote! { |
273 | pub default unsafe impl union {} |
274 | }; |
275 | |
276 | snapshot!(tokens as Item, @"Verbatim(`pub default unsafe impl union { }`)" ); |
277 | } |
278 | |
279 | #[test] |
280 | fn test_impl_type_parameter_defaults() { |
281 | #[cfg (any())] |
282 | impl<T = ()> () {} |
283 | let tokens = quote! { |
284 | impl<T = ()> () {} |
285 | }; |
286 | snapshot!(tokens as Item, @r###" |
287 | Item::Impl { |
288 | generics: Generics { |
289 | lt_token: Some, |
290 | params: [ |
291 | Type(TypeParam { |
292 | ident: "T", |
293 | eq_token: Some, |
294 | default: Some(Type::Tuple), |
295 | }), |
296 | ], |
297 | gt_token: Some, |
298 | }, |
299 | self_ty: Type::Tuple, |
300 | }"### ); |
301 | } |
302 | |
303 | #[test] |
304 | fn test_impl_trait_trailing_plus() { |
305 | let tokens = quote! { |
306 | fn f() -> impl Sized + {} |
307 | }; |
308 | |
309 | snapshot!(tokens as Item, @r###" |
310 | Item::Fn { |
311 | vis: Inherited, |
312 | sig: Signature { |
313 | ident: "f", |
314 | generics: Generics, |
315 | output: Type( |
316 | Type::ImplTrait { |
317 | bounds: [ |
318 | Trait(TraitBound { |
319 | modifier: None, |
320 | path: Path { |
321 | segments: [ |
322 | PathSegment { |
323 | ident: "Sized", |
324 | arguments: None, |
325 | }, |
326 | ], |
327 | }, |
328 | }), |
329 | ], |
330 | }, |
331 | ), |
332 | }, |
333 | block: Block, |
334 | } |
335 | "### ); |
336 | } |
337 | |