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