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