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<'a, 'ast> Visit<'ast> for TypeVisitor<'a> {
21 fn visit_lifetime(&mut self, lt: &'ast Lifetime) {
22 if lt.ident != "static" {
23 self.found_lifetimes = true;
24 }
25 visit_lifetime(self, node: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: &Ident) = ty.path.get_ident() {
32 if self.typarams.contains(ident) {
33 self.found_typarams = true;
34 }
35 }
36
37 visit_type_path(self, node: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<'_> = TypeVisitor {
45 typarams,
46 found_typarams: false,
47 found_lifetimes: false,
48 };
49 visit_type(&mut visit, node: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}
114