blob: 27b204e9acb91bab4ad80f35a8cae43a39855d6d [file] [log] [blame]
David Tolnaye7678922016-10-13 20:44:03 -07001extern crate quote;
David Tolnay7d8b3312019-03-10 01:26:11 -08002extern crate syn;
David Tolnayb153dbc2016-10-04 23:39:10 -07003
David Tolnay7d8b3312019-03-10 01:26:11 -08004mod features;
Alex Crichton605643b2017-07-05 18:35:14 -07005
David Tolnaydd125562017-12-31 02:16:22 -05006#[macro_use]
7mod macros;
8
David Tolnay7d8b3312019-03-10 01:26:11 -08009use quote::quote;
10use syn::{DeriveInput, ItemFn, TypeParamBound, WhereClause, WherePredicate};
Alex Crichtoneed4bc72018-05-17 10:59:15 -070011
David Tolnayb153dbc2016-10-04 23:39:10 -070012#[test]
13fn test_split_for_impl() {
David Tolnayfcd53cf2019-05-09 13:06:59 -070014 let input = quote! {
David Tolnay7d8b3312019-03-10 01:26:11 -080015 struct S<'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug;
David Tolnayb153dbc2016-10-04 23:39:10 -070016 };
17
David Tolnayfcd53cf2019-05-09 13:06:59 -070018 snapshot!(input as DeriveInput, @r###"
19 DeriveInput {
20 vis: Inherited,
21 ident: "S",
22 generics: Generics {
23 lt_token: Some,
24 params: [
25 Lifetime(LifetimeDef {
26 lifetime: Lifetime {
27 ident: "a",
28 },
29 }),
30 Lifetime(LifetimeDef {
31 lifetime: Lifetime {
32 ident: "b",
33 },
34 colon_token: Some,
35 bounds: [
36 Lifetime {
37 ident: "a",
38 },
39 ],
40 }),
41 Type(TypeParam {
42 attrs: [
43 Attribute {
44 style: Outer,
45 path: Path {
46 segments: [
47 PathSegment {
48 ident: "may_dangle",
49 arguments: None,
50 },
51 ],
52 },
53 tts: ``,
54 },
55 ],
56 ident: "T",
57 colon_token: Some,
58 bounds: [
59 Lifetime(Lifetime {
60 ident: "a",
61 }),
62 ],
63 eq_token: Some,
64 default: Some(Type::Tuple),
65 }),
66 ],
67 gt_token: Some,
68 where_clause: Some(WhereClause {
69 predicates: [
70 Type(PredicateType {
71 bounded_ty: Type::Path {
72 path: Path {
73 segments: [
74 PathSegment {
75 ident: "T",
76 arguments: None,
77 },
78 ],
79 },
80 },
81 bounds: [
82 Trait(TraitBound {
83 modifier: None,
84 path: Path {
85 segments: [
86 PathSegment {
87 ident: "Debug",
88 arguments: None,
89 },
90 ],
91 },
92 }),
93 ],
94 }),
95 ],
96 }),
97 },
98 data: Data::Struct {
99 fields: Unit,
100 semi_token: Some,
101 },
102 ⋮}
103 "###);
David Tolnay7d8b3312019-03-10 01:26:11 -0800104
David Tolnayfcd53cf2019-05-09 13:06:59 -0700105 let generics = input.generics;
David Tolnayb153dbc2016-10-04 23:39:10 -0700106 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
David Tolnay7d8b3312019-03-10 01:26:11 -0800107
108 let generated = quote! {
David Tolnaye7678922016-10-13 20:44:03 -0700109 impl #impl_generics MyTrait for Test #ty_generics #where_clause {}
David Tolnayb153dbc2016-10-04 23:39:10 -0700110 };
David Tolnay7d8b3312019-03-10 01:26:11 -0800111 let expected = quote! {
112 impl<'a, 'b: 'a, #[may_dangle] T: 'a> MyTrait
113 for Test<'a, 'b, T>
114 where
115 T: Debug
116 {}
117 };
118 assert_eq!(generated.to_string(), expected.to_string());
David Tolnayb153dbc2016-10-04 23:39:10 -0700119
David Tolnayc879a502017-01-25 15:51:32 -0800120 let turbofish = ty_generics.as_turbofish();
David Tolnay7d8b3312019-03-10 01:26:11 -0800121 let generated = quote! {
David Tolnayc879a502017-01-25 15:51:32 -0800122 Test #turbofish
123 };
David Tolnay7d8b3312019-03-10 01:26:11 -0800124 let expected = quote! {
125 Test::<'a, 'b, T>
126 };
127 assert_eq!(generated.to_string(), expected.to_string());
David Tolnayb153dbc2016-10-04 23:39:10 -0700128}
David Tolnay23d83f92017-01-25 15:41:47 -0800129
130#[test]
131fn test_ty_param_bound() {
132 let tokens = quote!('a);
David Tolnayfcd53cf2019-05-09 13:06:59 -0700133 snapshot!(tokens as TypeParamBound, @r###"
134 Lifetime(Lifetime {
135 ident: "a",
136 ⋮})
137 "###);
David Tolnay23d83f92017-01-25 15:41:47 -0800138
Bastien Orivel340553a2018-02-15 23:57:38 +0100139 let tokens = quote!('_);
David Tolnayfcd53cf2019-05-09 13:06:59 -0700140 snapshot!(tokens as TypeParamBound, @r###"
141 Lifetime(Lifetime {
142 ident: "_",
143 ⋮})
144 "###);
Bastien Orivel340553a2018-02-15 23:57:38 +0100145
David Tolnay23d83f92017-01-25 15:41:47 -0800146 let tokens = quote!(Debug);
David Tolnayfcd53cf2019-05-09 13:06:59 -0700147 snapshot!(tokens as TypeParamBound, @r###"
148 Trait(TraitBound {
149 modifier: None,
150 path: Path {
151 segments: [
152 PathSegment {
153 ident: "Debug",
154 arguments: None,
155 },
156 ],
157 },
158 ⋮})
159 "###);
David Tolnay23d83f92017-01-25 15:41:47 -0800160
161 let tokens = quote!(?Sized);
David Tolnayfcd53cf2019-05-09 13:06:59 -0700162 snapshot!(tokens as TypeParamBound, @r###"
163 Trait(TraitBound {
164 modifier: Maybe,
165 path: Path {
166 segments: [
167 PathSegment {
168 ident: "Sized",
169 arguments: None,
170 },
171 ],
172 },
173 ⋮})
174 "###);
David Tolnay23d83f92017-01-25 15:41:47 -0800175}
Geoffry Songac02b182018-05-19 22:11:31 -0700176
177#[test]
178fn test_fn_precedence_in_where_clause() {
David Tolnayc8b0e0f2019-03-07 22:46:32 -0800179 // This should parse as two separate bounds, `FnOnce() -> i32` and `Send` - not
180 // `FnOnce() -> (i32 + Send)`.
David Tolnayfcd53cf2019-05-09 13:06:59 -0700181 let input = quote! {
David Tolnay65fb5662018-05-20 20:02:28 -0700182 fn f<G>()
183 where
184 G: FnOnce() -> i32 + Send,
185 {
186 }
187 };
David Tolnay7d8b3312019-03-10 01:26:11 -0800188
David Tolnayfcd53cf2019-05-09 13:06:59 -0700189 snapshot!(input as ItemFn, @r###"
190 ItemFn {
191 vis: Inherited,
192 ident: "f",
193 decl: FnDecl {
194 generics: Generics {
195 lt_token: Some,
196 params: [
197 Type(TypeParam {
198 ident: "G",
199 }),
200 ],
201 gt_token: Some,
202 where_clause: Some(WhereClause {
203 predicates: [
204 Type(PredicateType {
205 bounded_ty: Type::Path {
206 path: Path {
207 segments: [
208 PathSegment {
209 ident: "G",
210 arguments: None,
211 },
212 ],
213 },
214 },
215 bounds: [
216 Trait(TraitBound {
217 modifier: None,
218 path: Path {
219 segments: [
220 PathSegment {
221 ident: "FnOnce",
222 arguments: PathArguments::Parenthesized {
223 output: Type(
224 Type::Path {
225 path: Path {
226 segments: [
227 PathSegment {
228 ident: "i32",
229 arguments: None,
230 },
231 ],
232 },
233 },
234 ),
235 },
236 },
237 ],
238 },
239 }),
240 Trait(TraitBound {
241 modifier: None,
242 path: Path {
243 segments: [
244 PathSegment {
245 ident: "Send",
246 arguments: None,
247 },
248 ],
249 },
250 }),
251 ],
252 }),
253 ],
254 }),
255 },
256 output: Default,
257 },
258 block: Block,
259 ⋮}
260 "###);
David Tolnay7d8b3312019-03-10 01:26:11 -0800261
David Tolnayfcd53cf2019-05-09 13:06:59 -0700262 let where_clause = input.decl.generics.where_clause.as_ref().unwrap();
Geoffry Songac02b182018-05-19 22:11:31 -0700263 assert_eq!(where_clause.predicates.len(), 1);
David Tolnay7d8b3312019-03-10 01:26:11 -0800264
265 let predicate = match &where_clause.predicates[0] {
266 WherePredicate::Type(pred) => pred,
Geoffry Songac02b182018-05-19 22:11:31 -0700267 _ => panic!("wrong predicate kind"),
268 };
David Tolnay7d8b3312019-03-10 01:26:11 -0800269
Geoffry Songac02b182018-05-19 22:11:31 -0700270 assert_eq!(predicate.bounds.len(), 2, "{:#?}", predicate.bounds);
David Tolnay7d8b3312019-03-10 01:26:11 -0800271
Geoffry Songac02b182018-05-19 22:11:31 -0700272 let first_bound = &predicate.bounds[0];
273 assert_eq!(quote!(#first_bound).to_string(), "FnOnce ( ) -> i32");
David Tolnay7d8b3312019-03-10 01:26:11 -0800274
Geoffry Songac02b182018-05-19 22:11:31 -0700275 let second_bound = &predicate.bounds[1];
276 assert_eq!(quote!(#second_bound).to_string(), "Send");
277}
David Tolnay38012de2018-09-02 13:32:47 -0700278
279#[test]
280fn test_where_clause_at_end_of_input() {
David Tolnayfcd53cf2019-05-09 13:06:59 -0700281 let input = quote! {
David Tolnay38012de2018-09-02 13:32:47 -0700282 where
283 };
David Tolnay7d8b3312019-03-10 01:26:11 -0800284
David Tolnayfcd53cf2019-05-09 13:06:59 -0700285 snapshot!(input as WhereClause, @"WhereClause");
286
287 assert_eq!(input.predicates.len(), 0);
David Tolnay38012de2018-09-02 13:32:47 -0700288}