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] |
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] |
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] |
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] |
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 | |