1 #[macro_use]
2 mod macros;
3 
4 use quote::quote;
5 use syn::{DeriveInput, ItemFn, TypeParamBound, WhereClause, WherePredicate};
6 
7 #[test]
test_split_for_impl()8 fn test_split_for_impl() {
9     let input = quote! {
10         struct S<'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug;
11     };
12 
13     snapshot!(input as DeriveInput, @r###"
14     DeriveInput {
15         vis: Inherited,
16         ident: "S",
17         generics: Generics {
18             lt_token: Some,
19             params: [
20                 Lifetime(LifetimeDef {
21                     lifetime: Lifetime {
22                         ident: "a",
23                     },
24                 }),
25                 Lifetime(LifetimeDef {
26                     lifetime: Lifetime {
27                         ident: "b",
28                     },
29                     colon_token: Some,
30                     bounds: [
31                         Lifetime {
32                             ident: "a",
33                         },
34                     ],
35                 }),
36                 Type(TypeParam {
37                     attrs: [
38                         Attribute {
39                             style: Outer,
40                             path: Path {
41                                 segments: [
42                                     PathSegment {
43                                         ident: "may_dangle",
44                                         arguments: None,
45                                     },
46                                 ],
47                             },
48                             tokens: TokenStream(``),
49                         },
50                     ],
51                     ident: "T",
52                     colon_token: Some,
53                     bounds: [
54                         Lifetime(Lifetime {
55                             ident: "a",
56                         }),
57                     ],
58                     eq_token: Some,
59                     default: Some(Type::Tuple),
60                 }),
61             ],
62             gt_token: Some,
63             where_clause: Some(WhereClause {
64                 predicates: [
65                     Type(PredicateType {
66                         bounded_ty: Type::Path {
67                             path: Path {
68                                 segments: [
69                                     PathSegment {
70                                         ident: "T",
71                                         arguments: None,
72                                     },
73                                 ],
74                             },
75                         },
76                         bounds: [
77                             Trait(TraitBound {
78                                 modifier: None,
79                                 path: Path {
80                                     segments: [
81                                         PathSegment {
82                                             ident: "Debug",
83                                             arguments: None,
84                                         },
85                                     ],
86                                 },
87                             }),
88                         ],
89                     }),
90                 ],
91             }),
92         },
93         data: Data::Struct {
94             fields: Unit,
95             semi_token: Some,
96         },
97     }
98     "###);
99 
100     let generics = input.generics;
101     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
102 
103     let generated = quote! {
104         impl #impl_generics MyTrait for Test #ty_generics #where_clause {}
105     };
106     let expected = quote! {
107         impl<'a, 'b: 'a, #[may_dangle] T: 'a> MyTrait
108         for Test<'a, 'b, T>
109         where
110             T: Debug
111         {}
112     };
113     assert_eq!(generated.to_string(), expected.to_string());
114 
115     let turbofish = ty_generics.as_turbofish();
116     let generated = quote! {
117         Test #turbofish
118     };
119     let expected = quote! {
120         Test::<'a, 'b, T>
121     };
122     assert_eq!(generated.to_string(), expected.to_string());
123 }
124 
125 #[test]
test_ty_param_bound()126 fn test_ty_param_bound() {
127     let tokens = quote!('a);
128     snapshot!(tokens as TypeParamBound, @r###"
129     Lifetime(Lifetime {
130         ident: "a",
131     })
132     "###);
133 
134     let tokens = quote!('_);
135     snapshot!(tokens as TypeParamBound, @r###"
136     Lifetime(Lifetime {
137         ident: "_",
138     })
139     "###);
140 
141     let tokens = quote!(Debug);
142     snapshot!(tokens as TypeParamBound, @r###"
143     Trait(TraitBound {
144         modifier: None,
145         path: Path {
146             segments: [
147                 PathSegment {
148                     ident: "Debug",
149                     arguments: None,
150                 },
151             ],
152         },
153     })
154     "###);
155 
156     let tokens = quote!(?Sized);
157     snapshot!(tokens as TypeParamBound, @r###"
158     Trait(TraitBound {
159         modifier: Maybe,
160         path: Path {
161             segments: [
162                 PathSegment {
163                     ident: "Sized",
164                     arguments: None,
165                 },
166             ],
167         },
168     })
169     "###);
170 }
171 
172 #[test]
test_fn_precedence_in_where_clause()173 fn test_fn_precedence_in_where_clause() {
174     // This should parse as two separate bounds, `FnOnce() -> i32` and `Send` - not
175     // `FnOnce() -> (i32 + Send)`.
176     let input = quote! {
177         fn f<G>()
178         where
179             G: FnOnce() -> i32 + Send,
180         {
181         }
182     };
183 
184     snapshot!(input as ItemFn, @r###"
185     ItemFn {
186         vis: Inherited,
187         sig: Signature {
188             ident: "f",
189             generics: Generics {
190                 lt_token: Some,
191                 params: [
192                     Type(TypeParam {
193                         ident: "G",
194                     }),
195                 ],
196                 gt_token: Some,
197                 where_clause: Some(WhereClause {
198                     predicates: [
199                         Type(PredicateType {
200                             bounded_ty: Type::Path {
201                                 path: Path {
202                                     segments: [
203                                         PathSegment {
204                                             ident: "G",
205                                             arguments: None,
206                                         },
207                                     ],
208                                 },
209                             },
210                             bounds: [
211                                 Trait(TraitBound {
212                                     modifier: None,
213                                     path: Path {
214                                         segments: [
215                                             PathSegment {
216                                                 ident: "FnOnce",
217                                                 arguments: PathArguments::Parenthesized {
218                                                     output: Type(
219                                                         Type::Path {
220                                                             path: Path {
221                                                                 segments: [
222                                                                     PathSegment {
223                                                                         ident: "i32",
224                                                                         arguments: None,
225                                                                     },
226                                                                 ],
227                                                             },
228                                                         },
229                                                     ),
230                                                 },
231                                             },
232                                         ],
233                                     },
234                                 }),
235                                 Trait(TraitBound {
236                                     modifier: None,
237                                     path: Path {
238                                         segments: [
239                                             PathSegment {
240                                                 ident: "Send",
241                                                 arguments: None,
242                                             },
243                                         ],
244                                     },
245                                 }),
246                             ],
247                         }),
248                     ],
249                 }),
250             },
251             output: Default,
252         },
253         block: Block,
254     }
255     "###);
256 
257     let where_clause = input.sig.generics.where_clause.as_ref().unwrap();
258     assert_eq!(where_clause.predicates.len(), 1);
259 
260     let predicate = match &where_clause.predicates[0] {
261         WherePredicate::Type(pred) => pred,
262         _ => panic!("wrong predicate kind"),
263     };
264 
265     assert_eq!(predicate.bounds.len(), 2, "{:#?}", predicate.bounds);
266 
267     let first_bound = &predicate.bounds[0];
268     assert_eq!(quote!(#first_bound).to_string(), "FnOnce () -> i32");
269 
270     let second_bound = &predicate.bounds[1];
271     assert_eq!(quote!(#second_bound).to_string(), "Send");
272 }
273 
274 #[test]
test_where_clause_at_end_of_input()275 fn test_where_clause_at_end_of_input() {
276     let input = quote! {
277         where
278     };
279 
280     snapshot!(input as WhereClause, @"WhereClause");
281 
282     assert_eq!(input.predicates.len(), 0);
283 }
284