owned this note
owned this note
Published
Linked with GitHub
# Bad macro diagnostics
## Just a simple typo
```rust
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
```rust!
macro_rules! uwu {
(uwu) => {};
}
macro_rules! forward_uwus {
($uwu:expr) => { uwu! { $uwu } };
}
forward_uwus!(uwu);
```
### On the matcher side
```rust
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:
```rust
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 --+
```
```rust
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:
```rust
#[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:
```rust
{
let_x_42!();
dbg!(x);
}
{
let mut x = 42;
clear_x!();
}
```
But if one then does:
```rust
macro_rules! let_x_42_proxied {( $($tt:tt)* ) => (
$crate::proc_macros::let_x_42! { $($tt)* }
)}
// ditto for `clear_x`
```
then the following fails:
```rust
{
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
```rust
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 `$`
```rust!
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
```