我有一个macro_rules
,它接受function_name
并调用function_name_x()
。现在我通过传递function_name
作为ident
,并通过concat_idents!
创建新的函数名来做到这一点。
这种方法的问题是我的IDE不检测function_name
部分,因为它将其作为ident
。我想将其更改为ty
,但如果我这样做,我不能在concat_idents!
中使用它。
有解决方案吗?
macro_rules! decl_func {
($env:expr, $func:ident) => {{
let f = concat_idents!($func, _to_ocaml);
let binding = f($env, None);
println!("{}", binding);
}};
}
根据您的使用情况,我认为ident
是正确的选择。一般来说,path
、tt
、expr
可能适合接受一个函数,因为你只是用它来创建另一个标识符,而ident
是最有意义的。
通过复杂的蒙特卡罗分析,rust-analyzer处理宏语法检测的方式似乎是首先展开宏,评估生成的代码,然后将该信息反向引用到原始宏参数。
在下面的示例中,decl_func1
调用正确地将function_name
标识符链接到函数定义,而decl_func2
调用没有。
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);
}
因此,在您的代码中,rust-analyzer不会连接到function_name
,因为生成的代码只有function_name_x
。
一个潜在的解决方法是在宏中引入一条语句,该语句在语法上按原样使用标识符,但在某种程度上它最终什么也不做。无论如何,这可能是一个好主意,这样编译器(而不仅仅是rust-analyzer)可以验证标识符是否应该是什么。几个想法:
// 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);