Bad macro diagnostics

Just a simple typo

macro_rules! uwu {
    (a a a a) => {};
}

uwu!(a a b a);

Addressed by https://github.com/rust-lang/rust/pull/103439

Implicit token groupings breaking things

On the caller side

macro_rules! uwu {
    (uwu) => {};
}

macro_rules! forward_uwus {
    ($uwu:expr) => { uwu! { $uwu } };
}

forward_uwus!(uwu);

On the matcher side

macro_rules! mk_matcher {( $_0:literal ) => (
    macro_rules! matcher {( $_0 ) => ()}
)}
mk_matcher!(0);

matcher!(0);

Maybe a lint of this happens in matcher position?

Hygiene errors are too hard to fix

  • Generally, hygiene is too invisible and its semantics, not known/taught enough.

  • So when a subtle hygiene error occurs, it can be painstakingly difficult to fix.

  • Mainly, there is no debugging tool to show hygiene: invisible during expansions, and the expanded,hygiene pass is just unreadable, even for macro-savy people.

  • For instance, hygiene errors do not mention the presence of equally named items in scope that are unaccessible due to hygiene;

    • It would be really nice if there was some reporting tool that mentioned smth along the lines of:

      ​​​​​​​​macro_rules! let_x_42 {() => (
      ​​​​​​​​    let x = 42; // <--- 4. is defined here, with private hygiene -+
      ​​​​​​​​)}                                                             // |
      ​​​​​​​​                                                               // |
      ​​​​​​​​let_x_42!(); // <--- 3. created by this macro invocation, which --+
      ​​​​​​​​                                                               // |
      ​​​​​​​​dbg!(x); // 1. Error, no `x` in scope                          // |
      ​​​​​​​​// 2. note: there is a macro-defined `x` in scope here, though  --+
      
      ​​​​​​​​macro_rules! clear_x {() => (
      ​​​​​​​​    x = 0; // <--- 4. but the `x` name here has private hygiene --+
      ​​​​​​​​)}                                                             // |
      ​​​​​​​​                                                               // |
      ​​​​​​​​let mut x = 42; // <--------- 3. defined here --------------------+
      ​​​​​​​​                                                               // |
      ​​​​​​​​clear_x!(); // 1. Error, no `x` in scope in this macro invo    // |
      ​​​​​​​​// 2. note: there is a caller-defined `x` in scope here, though --+
      
  • This is especially problematic when an unhygienic proc-macro, say:

    ​​​​#[proc_macro] pub
    ​​​​fn let_x_42(_: TokenStream) -> TokenStream {
    ​​​​    stringify!(
    ​​​​        let x = 42;
    ​​​​    )
    ​​​​    .parse().unwrap() // defaults to unhygienic `Span::call_site()`
    ​​​​}
    ​​​​
    ​​​​#[proc_macro] pub
    ​​​​fn clear_x(_: TokenStream) -> TokenStream {
    ​​​​    stringify!(
    ​​​​        x = 0;
    ​​​​    )
    ​​​​    .parse().unwrap() // defaults to unhygienic `Span::call_site()`
    ​​​​}
    

    Both of these will work when doing:

    ​​​​{
    ​​​​    let_x_42!();
    ​​​​    dbg!(x);
    ​​​​}
    ​​​​
    ​​​​{
    ​​​​    let mut x = 42;
    ​​​​    clear_x!();
    ​​​​}
    

    But if one then does:

    ​​​​macro_rules! let_x_42_proxied {( $($tt:tt)* ) => (
    ​​​​    $crate::proc_macros::let_x_42! { $($tt)* }
    ​​​​)}
    ​​​​// ditto for `clear_x`
    

    then the following fails:

    ​​​​{
    ​​​​    let_x_42!();
    ​​​​    dbg!(x); // Error, no `x` in scope!
    ​​​​}
    ​​​​
    ​​​​{
    ​​​​    let mut x = 42;
    ​​​​    clear_x!(); // Error in the macro invocation: no `x` in scope!
    ​​​​}
    

    The reason for this is quite subtle, and with no diagnostics whatsoever, quite puzzling and even disheartening (see https://discord.com/channels/273534239310479360/512792629516173323/1039234673009762314): the thing is that the unhygienic call_site() of these proc-macros stems from the path::to::macro_name, !, {} tokens used to invoke it. And these tokens are created at the definition site of the proxy macros. Since the definition site of a macro_rules! macro is semitransparent / is like Span::mixed_site() for a proc-macro, we end up with the situation wherein by funneling a proc-macro through a macro_rules! macro, it becomes more hygienic than it used to be.

    • So with the idea of enhanced diagnostics, it would be awesome if the compiler illustrated this: saying that the call_site() hygiene of the x forged by the proc-macro matches the span of $crate::proc_macros::let_x_42! {, and that these tokens, by virtue of being literally written within the definition of a macro_rules! macros, have "private hygiene" w.r.t. the call-site of this proxy macro.

Wrong depth repetetition errors are incomprehensible

macro_rules! uwu {
  ($($t:tt)*) => { $($t, $($t)*)* } 
}

uwu!(a);
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
 --> src/lib.rs:2:27
  |
2 |   ($($t:tt)*) => { $($t, $($t)*)* } 
  |                           ^^^^

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

Missed $

macro_rules! long_list {
    ($(finish:ident)*) => {};
}

long_list! { T }
error: no rules expected the token `T`
 --> uwu.rs:7:15
  |
3 | macro_rules! long_list {
  | ---------------------- when calling this macro
...
7 | long_list! {  T }
  |               ^ no rules expected this token in macro call
  |
  = note: while trying to match sequence start
Select a repo