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