Don't give "no rules expected the token" errors for macros that failed to parse

edb3d37
Opened by scottmcm at 2024-10-08 18:22:38

Given the following code: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=293168337d6a7ce2c74a82461e536bad

macro_rules! foo {
    ($t:type) => {};
}

foo! { Option<bool> }

The current output is:

error: invalid fragment specifier `type`
 --> src/lib.rs:2:6
  |
2 |     ($t:type) => {};
  |      ^^^^^^^
  |
  = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`

error: no rules expected the token `<`
 --> src/lib.rs:5:14
  |
1 | macro_rules! foo {
  | ---------------- when calling this macro
...
5 | foo! { Option<bool> }
  |              ^ no rules expected this token in macro call

error: could not compile `playground` due to 2 previous errors
<!-- The following is not always necessary. -->

Ideally the output should look like:

error: invalid fragment specifier `type`
 --> src/lib.rs:2:6
  |
2 |     ($t:type) => {};
  |      ^^^^^^^
  |
  = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`

error: could not compile `playground` due to previous error

Because telling me the call to the macro is invalid doesn't make much sense when that's impossible to fix without fixing the macro first.


Non-minimized mistake I made that inspired me to open this issue: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=aa7ce077f0b05e2af987f72539983e87

macro_rules! impl_for_optional_bool {
    ($($t:type,)+) => {$(
        unsafe impl IsZero for $t {
            fn is_zero(&self) -> bool {
                // SAFETY: This is *not* a stable layout guarantee, but
                // inside `core` we're allowed to rely on the current rustc
                // behaviour that options of bools will be one byte with
                // no padding, so long as they're nested less than 254 deep.
                let raw: u8 = unsafe { core::mem::transmute(*self) };
                raw == 0
            }
        }
    )+};
}

impl_for_optional_bool! {
    Option<bool>,
    Option<Option<bool>>,
    Option<Option<Option<bool>>>,
    // Could go further, but not worth the trait lookup overhead
}
  1. This case now has an even more confusing error message:

    macro_rules! m {
        ($a:lit) => {};
    }
    
    pub fn main() {
        m!(42);
    }
    

    will show a note claiming that $a was declared as $a:ident:

    error: invalid fragment specifier `lit`
     --> <source>:2:6
      |
    2 |     ($a:lit) => {};
      |      ^^^^^^
      |
      = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
    
    error: no rules expected the token `42`
     --> <source>:6:8
      |
    1 | macro_rules! m {
      | -------------- when calling this macro
    ...
    6 |     m!(42);
      |        ^^ no rules expected this token in macro call
      |
    note: while trying to match meta-variable `$a:ident`
     --> <source>:2:6
      |
    2 |     ($a:lit) => {};
      |      ^^^^^^
    
    error: aborting due to 2 previous errors
    

    (Godbolt link)

    $ rustc --version --verbose
    rustc 1.83.0-nightly (3ae715c8c 2024-10-07)
    binary: rustc
    commit-hash: 3ae715c8c63f9aeac47cbf7d8d9dadb3fa32c638
    commit-date: 2024-10-07
    host: x86_64-unknown-linux-gnu
    release: 1.83.0-nightly
    LLVM version: 19.1.1
    

    narpfel at 2024-10-08 18:22:38