// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). //! Visitor for determining whether a type has type and non-static lifetime parameters //! (duplicated in yoke/derive/src/visitor.rs) use std::collections::HashSet; use syn::visit::{visit_lifetime, visit_type, visit_type_path, Visit}; use syn::{Ident, Lifetime, Type, TypePath}; struct TypeVisitor<'a> { /// The type parameters in scope typarams: &'a HashSet, /// Whether we found a type parameter found_typarams: bool, /// Whether we found a non-'static lifetime parameter found_lifetimes: bool, } impl<'a, 'ast> Visit<'ast> for TypeVisitor<'a> { fn visit_lifetime(&mut self, lt: &'ast Lifetime) { if lt.ident != "static" { self.found_lifetimes = true; } visit_lifetime(self, lt) } fn visit_type_path(&mut self, ty: &'ast TypePath) { // We only need to check ty.path.get_ident() and not ty.qself or any // generics in ty.path because the visitor will eventually visit those // types on its own if let Some(ident) = ty.path.get_ident() { if self.typarams.contains(ident) { self.found_typarams = true; } } visit_type_path(self, ty) } } /// Checks if a type has type or lifetime parameters, given the local context of /// named type parameters. Returns (has_type_params, has_lifetime_params) pub fn check_type_for_parameters(ty: &Type, typarams: &HashSet) -> (bool, bool) { let mut visit = TypeVisitor { typarams, found_typarams: false, found_lifetimes: false, }; visit_type(&mut visit, ty); (visit.found_typarams, visit.found_lifetimes) } #[cfg(test)] mod tests { use proc_macro2::Span; use std::collections::HashSet; use syn::{parse_quote, Ident}; use super::check_type_for_parameters; fn make_typarams(params: &[&str]) -> HashSet { params .iter() .map(|x| Ident::new(x, Span::call_site())) .collect() } #[test] fn test_simple_type() { let environment = make_typarams(&["T", "U", "V"]); let ty = parse_quote!(Foo<'a, T>); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, true)); let ty = parse_quote!(Foo); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, false)); let ty = parse_quote!(Foo<'static, T>); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, false)); let ty = parse_quote!(Foo<'a>); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (false, true)); let ty = parse_quote!(Foo<'a, Bar, Baz<(V, u8)>>); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, true)); let ty = parse_quote!(Foo<'a, W>); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (false, true)); } #[test] fn test_assoc_types() { let environment = make_typarams(&["T"]); let ty = parse_quote!(>::Output); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, true)); let ty = parse_quote!(>::Output); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, false)); let ty = parse_quote!(>::Output); let check = check_type_for_parameters(&ty, &environment); assert_eq!(check, (true, false)); } }