// 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::HashMap;
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 HashMap<Ident, Option<Ident>>,
/// 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 let Some(maybe_borrowed) =
self.typarams.get(ident) {
self.found_typarams =
true;
if maybe_borrowed.is_some() {
self.found_lifetimes =
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: &HashMap<Ident, Option<Ident>>,
) -> (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::HashMap;
use syn::{parse_quote, Ident};
use super::check_type_for_parameters;
fn make_typarams(params: &[&str]) -> HashMap<Ident, Option<Ident>> {
params
.iter()
.map(|x| (Ident::new(x, Span::call_site()), None))
.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<T>);
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<U>, 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!(<Foo
as SomeTrait<
'a, T>>::Output);
let check = check_type_for_parameters(&ty, &environment);
assert_eq!(check, (
true,
true));
let ty = parse_quote!(<Foo
as SomeTrait<
'static, T>>::Output);
let check = check_type_for_parameters(&ty, &environment);
assert_eq!(check, (
true,
false));
let ty = parse_quote!(<T
as SomeTrait<
'static, Foo>>::Output);
let check = check_type_for_parameters(&ty, &environment);
assert_eq!(check, (
true,
false));
}
}