Custom (syntax) errors in macros

I'd like to better handle errors within my custom macros.

In my program, there are some structs that must implement a certain trait otherwise certain methods won't be available and the compiler will fail.

#[derive(Foo)]
struct ValidStruct { ... }
impl X for ValidStruct {
  fn foo() { ... }
}

#[derive(Foo)]
struct InvalidStruct { ... }
// no X trait implemented

I would like for my macro to show an error message like "MyStruct does not implement X trait." when a macro is used on an invalid struct.

At first, I tried to get information about the implemented traits from syn::Type - with no success. My last try was using the syn::parse2(quote!{ invalidstruct::foo() }) trying to get an error, based on which I could then throw my own error message - also without success.

#[proc_macro_derive(X, attributes(y))]
pub fn describe(input: TokenStream) -> TokenStream {
    ...
    let foo = quote!({
        InvalidStruct::foo(); // catch this error
    });
    if foo.is_err() { panic!(...) } // throw custom message
    ...
}

I'd ask you to share your ideas and experiences on how to handle such (syntax) errors. Is this actually possible?

A pattern I've used before is to generate something some sort of no-op function that uses generics to
assert a particular trait is implemented.

Here is an example where we assert Normalize implements Transform<Tensor<f32>, Output = Tensor<f32>> and Transform<Tensor<u64>, Output = Tensor<f32>> (i.e. normalizing tensors of f32's or u64's always results in a f32 tensor).

const _: () = {
    fn assert_implements_transform<T, Inputs, Outputs>()
    where
        T: hotg_rune_proc_blocks::internal::Transform<Inputs, Output = Outputs>,
    {
    }

    fn transform_assertions() {
        assert_implements_transform::<
            Normalize,
            hotg_rune_proc_blocks::internal::Tensor<f32>,
            hotg_rune_proc_blocks::internal::Tensor<f32>,
        >();
        assert_implements_transform::<
            Normalize,
            hotg_rune_proc_blocks::internal::Tensor<u64>,
            hotg_rune_proc_blocks::internal::Tensor<f32>,
        >();
    }
};
2 Likes

Also, in this instance, unless the example has been simplified too much, it looks like X would be a sensible supertrait of Foo:

trait X { /* … */ }

trait Foo : X { /* … */ }
macro_rules! Foo {(
    struct $StructName:ident $($_:tt)*
) => (
    impl $crate::Foo for $StructName {
        /* … */
    }
)}

#[macro_rules_derive(Foo!)]
struct InvalidStruct;
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.