Weird macro expansion

Take this cute macro (Rust Playground):

macro_rules! m {
    ( $($x:stmt;)* ) => { $($x)* };
}

fn main() {
    m!(4;5;);
}

To my surprise, the code expands the macro successfully and parses! I'd expect that it'll error due to not using semicolons within the macro body. This alone could perhaps be explained by saying that the stmt fragment, despite not capturing the trailing semicolon, acts like it's present - somewhat similar to invisible groups.

But this is only the beginning of the weirdness, because we get this totally unexpected compile error:

error[E0308]: mismatched types
 --> src/main.rs:6:8
  |
6 |     m!(4;5;);
  |        ^ expected `()`, found integer

For more information about this error, try `rustc --explain E0308`.

So somehow the compiler thinks our statements are not just 4; 5;.

And if we try to "fix" the macro by adding semicolons, we get (Rust Playground):

macro_rules! m {
    ( $($x:stmt;)* ) => { $($x;)* };
}

fn main() {
    m!(4;5;);
}
warning: unnecessary trailing semicolon
 --> src/main.rs:2:31
  |
2 |     ( $($x:stmt;)* ) => { $($x;)* };
  |                               ^
...
6 |     m!(4;5;);
  |     -------- in this macro invocation
  |
  = note: `#[warn(redundant_semicolons)]` (part of `#[warn(unused)]`) on by default
  = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
 --> src/main.rs:6:8
  |
6 |     m!(4;5;);
  |        ^ expected `()`, found integer

error[E0308]: mismatched types
 --> src/main.rs:6:10
  |
6 |     m!(4;5;);
  |          ^ expected `()`, found integer

So the compiler thinks our semicolons are redundant, but we also get two "mismatched types" error instead of one.

I reported this same issue a couple weeks ago:

1 Like

I suspect this bug went unnoticed for a long time because it is very rare to use stmt in macros — in my experience, most macros that have user-provided executable code (as opposed to types, literals, etc.) are accepting exprs or blocks, not stmts.