"pattern" matching in macro stopped after first failed attempt

These two snippets

macro_rules! testmacro {
    ( $p:pat = $($t:tt)* ) => {
        testmacro!(@ {$p = $($t)*} )
    };    
    (@{$res:pat = $some_expr:expr => $handle:block, $($t:tt)*}) => {
       testmacro!(@ {$($t)*})
    };
    (@ {else => $else:expr$(,)?}) =>{
        testmacro!(@)
    };
    (@)=> {1}
}

fn main(){
    let val = testmacro!(
     Some(mut foo) =  { Some("1".to_string()) } => {
           assert_eq!(foo, "1");
     },
    else => {2}
    );
    println!("{}",val)
}

and

macro_rules! testmacro {
    ( $p:pat = $($t:tt)* ) => {
        testmacro!(@ {$p = $($t)*} )
    };        
    (@ {else => $else:expr$(,)?}) =>{
        testmacro!(@)
    };
    (@{$res:pat = $async_expr:expr => $handle:block, $($t:tt)*}) => {
       testmacro!(@ {$($t)*})
    };

    (@)=> {1}
}

fn main(){
    let val = testmacro!(
     Some(mut foo) =  { Some("1".to_string()) } => {
           assert_eq!(foo, "1");
     },
    else => {2}
    );
    println!("{}",val)
}

seem to behave differently. The former would fail to compile with expected identifier, found keyword else while the latter would compile just fine.
I'm working on this PR: feat(WIP):attributes on select! branches by drHuangMHT · Pull Request #5398 · tokio-rs/tokio · GitHub and it involves macro that uses " Incremental TT munchers". tests would fail with expected identifier, found keyword else which is counter-intuitive because there is a branch that would catch else as keyword.
I'm using stable-x86_64-unknown-linux-gnu rustc 1.67.0 (fc594f156 2023-01-24) and the online playground poses the same problem.

The macro transcriber doesn't have any lookahead and explicitly tests each arm in order, so exceptions/special cases have to be listed before the general case.

Here, it appears that else is being matched by a $pat matcher which then immediately errors. I'm not familiar enough with Rust's formal grammar to be sure, but if else is actually forbidden as the first token in a pattern, then this seems like a bug in the definition of $pat— It should instead not match and allow the next branch to be tried.


Here's a reduced example of this behavior:

macro_rules! test_pattern_matcher {
    ($pat:pat) => ()
}

fn f() {
    // OK
    test_pattern_matcher!(x);
    
    // error: no rules expected token `+`
    //    (would check next match arm if present)
    test_pattern_matcher!(+);
    
    // error: expected identifier, found keyword `else`
    //    (matched, then errored, which stops parsing)
    test_pattern_matcher!(else);
}
2 Likes

I think treating keywords and indents the same here might have some advantages: it wouldn't be a breaking change to the macro_rules system to make certain keywords no longer be keywords. Otherwise, such a change would probably need to be accompanied with introduction of another legacy fragment specifier like pat_param for pat when patterns could no longer be terminated by “|”. Except, a bunch of them, one for each kind of fragment that previously could not start with the keyword in question, which could mean all of stmt, pat, expr, ty, and path.

1 Like

You can use r#else to use keyword else(or any other keyword) as an identifier. But that is basically a literal different from "else". However, the compiler will omit the "r#" in its output, but sometimes.

If this is a bug, I'll consider opening an issue on Rust GitHub repo. So what's your opinion?

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.