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