use proc_macro2::Ident;
use syn::{Data, DeriveInput, Type};
pub fn is_recursive(ast: &DeriveInput) -> bool {
let self_ident = &ast.ident;
match &ast.data {
Data::Struct(ds) => ds
.fields
.iter()
.any(|f| type_contains_self(&f.ty, self_ident)),
Data::Enum(de) => de.variants.iter().any(|v| {
v.fields
.iter()
.any(|f| type_contains_self(&f.ty, self_ident))
}),
Data::Union(_) => false,
}
}
fn type_contains_self(ty: &Type, self_ident: &Ident) -> bool {
match ty {
Type::Path(type_path) => {
let is_direct = type_path
.path
.segments
.last()
.map(|seg| seg.ident == *self_ident)
.unwrap_or(false);
let has_generic_self = type_path
.path
.segments
.iter()
.any(|seg| match &seg.arguments {
syn::PathArguments::AngleBracketed(ab) => ab.args.iter().any(|arg| match arg {
syn::GenericArgument::Type(t) => type_contains_self(t, self_ident),
_ => false,
}),
_ => false,
});
is_direct || has_generic_self
}
Type::Reference(r) => type_contains_self(&r.elem, self_ident),
Type::Ptr(p) => type_contains_self(&p.elem, self_ident),
Type::Slice(s) => type_contains_self(&s.elem, self_ident),
Type::Array(a) => type_contains_self(&a.elem, self_ident),
Type::Paren(p) => type_contains_self(&p.elem, self_ident),
Type::Tuple(t) => t.elems.iter().any(|e| type_contains_self(e, self_ident)),
Type::Group(g) => type_contains_self(&g.elem, self_ident),
_ => false,
}
}