rust 如何使用声明式宏嵌套调用其他函数式宏,是否可以正常工作?

cyej8jka  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(118)

我用一个声明性宏嵌套调用其他程序性宏,从arr![1_u32;2]arr_proc([1_u32;2])。我不想直接调用arr_proc!。但我可能有一些错误,当我编译生 rust 的代码。编译器似乎有时无法展开所有宏。
我的proc宏:

#[proc_macro]
pub fn arr_proc(input: TokenStream) -> TokenStream {
    let repeat_expr: ExprRepeat = parse(input)
        .expect("Like arr!([Test::default(); 16])");

    let mut len = 0;
    // get length from repeat_expr
    if let Expr::Lit(expr_lit) = repeat_expr.len.deref() {
        if let Lit::Int(int_lit) = &expr_lit.lit {
            len = int_lit.base10_parse::<usize>().expect("Failed to parse integer literal");
        }
    }
    // parse and concat
    let _expr = repeat_expr.expr;
    // 1. generate the arr
    let mut _all = quote!();
    for _i in 0..len {
        // 2. append element into arr
        _all = quote! { #_all #_expr, };
    }
    // 3. add []
    let arr = quote! { [ #_all ] };
    return arr.into();
}

我的声明性宏:

#[macro_export]
macro_rules! arr {
    ($ele:expr; $repeat:literal) => {
        custom_proc_macro::arr_proc!([$ele; $repeat])
    };
}

我的测试用例:

#[test]
fn test_arr_() {
    let a: [u32; 2] = arr![1_u32;2];
    dbg!(a);
}

错误是:

error[E0308]: mismatched types
  --> tests/custom_proc_macro_test.rs:48:23
   |
48 |     let a: [u32; 2] = arr![1_u32;2];
   |            --------   ^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 0 elements
   |            |     |
   |            |     help: consider specifying the actual array length: `0`
   |            expected due to this
   |
   = note: expected array `[u32; 2]`
              found array `[_; 0]`
   = note: this error originates in the macro `custom_proc_macro::arr_proc` which comes from the expansion of the macro `arr` (in Nightly builds, run with -Z macro-backtrace for more info)
The meaning probably the compiler expanded the declarative macro and did not expand the proc macro.

我怎么能这样用呢?它能工作吗?

flvlnr44

flvlnr441#

通过使用不带elseif-let,您可以隐藏所有可能的错误条件,这里的特定错误是声明性宏将文字 Package 在未分隔的Group s中,因此您的if let Expr::Lit(expr_lit) = ...会默默失败。
您仍然可以通过将整个if-let语句替换为以下内容来解析它:

let lit_expr = match *repeat_expr.len {
    Expr::Group(group) => match *group.expr {
        Expr::Lit(lit) => lit,
        _ => panic!("expected literal"),
    },
    Expr::Lit(lit) => lit,
    _ => panic!("expected literal or group"),
};
let len = if let Lit::Int(int_lit) = lit_expr.lit {
    int_lit
        .base10_parse::<usize>()
        .expect("Failed to parse integer literal")
} else {
    panic!("expected integer literal");
};

为了简洁起见,我在这里使用panic!,因为Span::error还不稳定。您可以阅读How to report errors in a procedural macro using the quote macro?来了解如何生成更好的错误消息。但你绝对不能像以前那样,对突发事件视而不见。

相关问题