yoke_derive/
visitor.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Visitor for determining whether a type has type and non-static lifetime parameters
6
7use std::collections::HashSet;
8use syn::visit::{visit_lifetime, visit_type, visit_type_path, Visit};
9use syn::{Ident, Lifetime, Type, TypePath};
10
11struct TypeVisitor<'a> {
12    /// The type parameters in scope
13    typarams: &'a HashSet<Ident>,
14    /// Whether we found a type parameter
15    found_typarams: bool,
16    /// Whether we found a non-'static lifetime parameter
17    found_lifetimes: bool,
18}
19
20impl<'ast> Visit<'ast> for TypeVisitor<'_> {
21    fn visit_lifetime(&mut self, lt: &'ast Lifetime) {
22        if lt.ident != "static" {
23            self.found_lifetimes = true;
24        }
25        visit_lifetime(self, lt)
26    }
27    fn visit_type_path(&mut self, ty: &'ast TypePath) {
28        // We only need to check ty.path.get_ident() and not ty.qself or any
29        // generics in ty.path because the visitor will eventually visit those
30        // types on its own
31        if let Some(ident) = ty.path.get_ident() {
32            if self.typarams.contains(ident) {
33                self.found_typarams = true;
34            }
35        }
36
37        visit_type_path(self, ty)
38    }
39}
40
41/// Checks if a type has type or lifetime parameters, given the local context of
42/// named type parameters. Returns (has_type_params, has_lifetime_params)
43pub fn check_type_for_parameters(ty: &Type, typarams: &HashSet<Ident>) -> (bool, bool) {
44    let mut visit = TypeVisitor {
45        typarams,
46        found_typarams: false,
47        found_lifetimes: false,
48    };
49    visit_type(&mut visit, ty);
50
51    (visit.found_typarams, visit.found_lifetimes)
52}
53
54#[cfg(test)]
55mod tests {
56    use proc_macro2::Span;
57    use std::collections::HashSet;
58    use syn::{parse_quote, Ident};
59
60    use super::check_type_for_parameters;
61    fn make_typarams(params: &[&str]) -> HashSet<Ident> {
62        params
63            .iter()
64            .map(|x| Ident::new(x, Span::call_site()))
65            .collect()
66    }
67
68    #[test]
69    fn test_simple_type() {
70        let environment = make_typarams(&["T", "U", "V"]);
71
72        let ty = parse_quote!(Foo<'a, T>);
73        let check = check_type_for_parameters(&ty, &environment);
74        assert_eq!(check, (true, true));
75
76        let ty = parse_quote!(Foo<T>);
77        let check = check_type_for_parameters(&ty, &environment);
78        assert_eq!(check, (true, false));
79
80        let ty = parse_quote!(Foo<'static, T>);
81        let check = check_type_for_parameters(&ty, &environment);
82        assert_eq!(check, (true, false));
83
84        let ty = parse_quote!(Foo<'a>);
85        let check = check_type_for_parameters(&ty, &environment);
86        assert_eq!(check, (false, true));
87
88        let ty = parse_quote!(Foo<'a, Bar<U>, Baz<(V, u8)>>);
89        let check = check_type_for_parameters(&ty, &environment);
90        assert_eq!(check, (true, true));
91
92        let ty = parse_quote!(Foo<'a, W>);
93        let check = check_type_for_parameters(&ty, &environment);
94        assert_eq!(check, (false, true));
95    }
96
97    #[test]
98    fn test_assoc_types() {
99        let environment = make_typarams(&["T"]);
100
101        let ty = parse_quote!(<Foo as SomeTrait<'a, T>>::Output);
102        let check = check_type_for_parameters(&ty, &environment);
103        assert_eq!(check, (true, true));
104
105        let ty = parse_quote!(<Foo as SomeTrait<'static, T>>::Output);
106        let check = check_type_for_parameters(&ty, &environment);
107        assert_eq!(check, (true, false));
108
109        let ty = parse_quote!(<T as SomeTrait<'static, Foo>>::Output);
110        let check = check_type_for_parameters(&ty, &environment);
111        assert_eq!(check, (true, false));
112    }
113}