• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
7 use std::collections::HashSet;
8 use syn::visit::{visit_lifetime, visit_type, visit_type_path, Visit};
9 use syn::{Ident, Lifetime, Type, TypePath};
10 
11 struct 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 
20 impl<'ast> Visit<'ast> for TypeVisitor<'_> {
visit_lifetime(&mut self, lt: &'ast Lifetime)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     }
visit_type_path(&mut self, ty: &'ast TypePath)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)
check_type_for_parameters(ty: &Type, typarams: &HashSet<Ident>) -> (bool, bool)43 pub 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)]
55 mod 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;
make_typarams(params: &[&str]) -> HashSet<Ident>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]
test_simple_type()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]
test_assoc_types()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 }
114