1#![allow(clippy::uninlined_format_args)]
2
3#[macro_use]
4mod macros;
5
6use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
7use quote::quote;
8use syn::{Item, ItemTrait};
9
10#[test]
11fn 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]
49fn 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]
122fn 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]
161fn 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]
239fn 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]
264fn 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]
273fn 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]
298fn 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