0

I use a declarative macro nested invocation other procedural macro, from arr![1_u32;2] to arr_proc([1_u32;2]). I don't want to call arr_proc! directly. But I may got something wrong, when I compile the rust code. It seems like the compiler can not expanded all the macro at some time.

My proc macro:

#[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();
}

My declarative macro:

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

My test case:

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

The error is:

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.

How can I use like this? Can it works?

Colagy
  • 1
  • It's a code smell to use `Deref::deref` with method syntax, just use `*repeat_expr.len` instead. – cafce25 Apr 26 '23 at 07:00

1 Answers1

0

By using if-let without an else you're silencing all possible error conditions, the particular error here is that the declarative macro wraps the literals in undelimited Groups and thus your if let Expr::Lit(expr_lit) = ... fails silently.
You can still parse it by replacing that whole if-let statement by something like this:

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");
};

I'm using panic! here for brevity and because Span::error is not stable yet. You can read How to report errors in a procedural macro using the quote macro? to see how to produce better error messages. But you definitely shouldn't silently ignore the unexpected cases like you did.

cafce25
  • 15,907
  • 4
  • 25
  • 31