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