use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
fn derive_type_matcher_impl(_attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
let item_impl: syn::ItemImpl = syn::parse2(input)?;
let trait_path = item_impl
.trait_
.as_ref()
.map(|(_, path, _)| path)
.ok_or_else(|| {
syn::Error::new(
item_impl.span(),
"#[type_matcher] must be applied to an `impl TypeMatcher for ...` block",
)
})?;
let trait_name = trait_path
.segments
.last()
.map(|seg| seg.ident.to_string())
.unwrap_or_default();
if trait_name != "TypeMatcher" {
return Err(syn::Error::new(
trait_path.span(),
"#[type_matcher] must be applied to an `impl TypeMatcher for ...` block",
));
}
let self_ty = &item_impl.self_ty;
let generics = &item_impl.generics;
let has_type_params = generics.type_params().next().is_some();
if has_type_params {
return Err(syn::Error::new(
generics.span(),
"#[type_matcher] does not support generic type parameters. \
For generic TypeMatcher implementations: \
1. Remove #[type_matcher] attribute from the impl block, \
2. Run build or `arc rust-check` with `pagable` feature to find call sites, \
3. Errors show types like `YourType<X>: TypeMatcherRegistered is not satisfied`. \
Two cases for X: \
a) Concrete type (e.g., `IsStr`, `IsNone`, `StarlarkTypeIdMatcher`) - use directly \
b) `impl TypeMatcher` - trace CALLERS to find concrete type, usually `TypeMatcherBox` \
from `TypeMatcherBoxAlloc.ty(...)` \
4. Add blanket unsafe impl: `unsafe impl<...> TypeMatcherRegistered for YourType<...> {}`, \
5. Add `register_type_matcher!(YourType<ConcreteArg>);` for each concrete instantiation. \
Example: IsListOf errors show IsListOf<IsStr> (case a) and IsListOf<impl TypeMatcher> (case b). \
Register both: `register_type_matcher!(IsListOf<IsStr>);` \
`register_type_matcher!(IsListOf<TypeMatcherBox>);`",
));
}
let registered_impl = quote! {
unsafe impl starlark::values::typing::TypeMatcherRegistered
for #self_ty
{}
};
let vtable_registration = quote! {
starlark::register_type_matcher!(#self_ty);
};
Ok(quote! {
#item_impl
#registered_impl
#vtable_registration
})
}
pub fn derive_type_matcher(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
match derive_type_matcher_impl(attr.into(), input.into()) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}