use std::collections::HashSet;
use syn::visit::{visit_lifetime, visit_type, visit_type_path, Visit};
use syn::{Ident, Lifetime, Type, TypePath};
struct TypeVisitor<'a> {
typarams: &'a HashSet<Ident>,
found_typarams: bool,
found_lifetimes: bool,
}
impl<'ast> Visit<'ast> for TypeVisitor<'_> {
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) {
if let Some(ident) = ty.path.get_ident() {
if self.typarams.contains(ident) {
self.found_typarams = true;
}
}
visit_type_path(self, ty)
}
}
pub fn check_type_for_parameters(ty: &Type, typarams: &HashSet<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::HashSet;
use syn::{parse_quote, Ident};
use super::check_type_for_parameters;
fn make_typarams(params: &[&str]) -> HashSet<Ident> {
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<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));
}
}