1 extern crate quote;
2 extern crate syn;
3
4 mod features;
5
6 #[macro_use]
7 mod macros;
8
9 use quote::quote;
10 use syn::{DeriveInput, ItemFn, TypeParamBound, WhereClause, WherePredicate};
11
12 #[test]
test_split_for_impl()13 fn test_split_for_impl() {
14 let input = quote! {
15 struct S<'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug;
16 };
17
18 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 "###);
104
105 let generics = input.generics;
106 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
107
108 let generated = quote! {
109 impl #impl_generics MyTrait for Test #ty_generics #where_clause {}
110 };
111 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());
119
120 let turbofish = ty_generics.as_turbofish();
121 let generated = quote! {
122 Test #turbofish
123 };
124 let expected = quote! {
125 Test::<'a, 'b, T>
126 };
127 assert_eq!(generated.to_string(), expected.to_string());
128 }
129
130 #[test]
test_ty_param_bound()131 fn test_ty_param_bound() {
132 let tokens = quote!('a);
133 snapshot!(tokens as TypeParamBound, @r###"
134 ⋮Lifetime(Lifetime {
135 ⋮ ident: "a",
136 ⋮})
137 "###);
138
139 let tokens = quote!('_);
140 snapshot!(tokens as TypeParamBound, @r###"
141 ⋮Lifetime(Lifetime {
142 ⋮ ident: "_",
143 ⋮})
144 "###);
145
146 let tokens = quote!(Debug);
147 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 "###);
160
161 let tokens = quote!(?Sized);
162 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 "###);
175 }
176
177 #[test]
test_fn_precedence_in_where_clause()178 fn test_fn_precedence_in_where_clause() {
179 // This should parse as two separate bounds, `FnOnce() -> i32` and `Send` - not
180 // `FnOnce() -> (i32 + Send)`.
181 let input = quote! {
182 fn f<G>()
183 where
184 G: FnOnce() -> i32 + Send,
185 {
186 }
187 };
188
189 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 "###);
261
262 let where_clause = input.decl.generics.where_clause.as_ref().unwrap();
263 assert_eq!(where_clause.predicates.len(), 1);
264
265 let predicate = match &where_clause.predicates[0] {
266 WherePredicate::Type(pred) => pred,
267 _ => panic!("wrong predicate kind"),
268 };
269
270 assert_eq!(predicate.bounds.len(), 2, "{:#?}", predicate.bounds);
271
272 let first_bound = &predicate.bounds[0];
273 assert_eq!(quote!(#first_bound).to_string(), "FnOnce ( ) -> i32");
274
275 let second_bound = &predicate.bounds[1];
276 assert_eq!(quote!(#second_bound).to_string(), "Send");
277 }
278
279 #[test]
test_where_clause_at_end_of_input()280 fn test_where_clause_at_end_of_input() {
281 let input = quote! {
282 where
283 };
284
285 snapshot!(input as WhereClause, @"WhereClause");
286
287 assert_eq!(input.predicates.len(), 0);
288 }
289