Convert a ty to an ident in a macro_rules

1.2k Views Asked by At

I have a macro_rules that takes a function_name and calls function_name_x(). Right now I do that by passing function_name as an ident, and creating the new function name via concat_idents!.

The problem with this approach is that my IDE does not lint the function_name part because it takes is as an ident. I'd like to change that to a ty but I can't use it in concat_idents! if I do that.

Is there a solution?

macro_rules! decl_func {
    ($env:expr, $func:ident) => {{
        let f = concat_idents!($func, _to_ocaml);
        let binding = f($env, None);
        println!("{}", binding);
    }};
}
1

There are 1 best solutions below

1
On

Judging by your usage, I would say ident is the correct choice. While other things like, path, tt, expr may be appropriate for accepting a function in general, since you're only using it to create another identifier, ident makes the most sense.


By way of sophisticated monte-carlo analysis, the way rust-analyzer seems to handle syntax detection in macros by expanding it first, assessing what code is generated, and then back-referencing that info to the original macro parameters.

In the following example, the decl_func1 call correctly links the function_name identifier to the function definition, while decl_func2 call does not.

fn function_name() {}

macro_rules! decl_func1 {
    ($func:ident) => {
        $func();
    };
}

macro_rules! decl_func2 {
    ($func:ident) => {};
}

fn main() {
    decl_func1!(function_name);
    decl_func2!(function_name);
}

So in your code, rust-analyzer won't make the connection to function_name because the generated code only has function_name_x.


A potential workaround would be to introduce a statement into the macro that would syntactically use the identifier as is, but in a way where it ultimately does nothing. This may be a good idea anyway so that the compiler, not just rust-analyzer, can verify the identifier is what it should be. A couple ideas:

// would check that it is some kind of value
let _ = &$func;
// would check that it is a function with a signature that can take these types
let _ = || $func(1, 2);