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