I have a little macro I wrote to help me explore what things are parsed as. With a few arms lopped off, it looks like:
macro_rules! show_me{
($l : literal $($tail : tt)*) => {
println!("{} is a literal and then I see {}", stringify!($l), stringify!($($tail)*));
show_me!($($tail)*)
};
(- $($tail : tt)*) => {
println!("{} is a - and then I see {}", stringify!(-), stringify!($($tail)*));
show_me!($($tail)*)
};
($i : ident $($tail : tt)*) => {
println!("{} is an ident and then I see {}", stringify!($i), stringify!($($tail)*));
show_me!($($tail)*)
};
($token : tt $($tail : tt)*) => {
println!("{} is a mystery and then I see {}", stringify!($token), stringify!($($tail)*));
show_me!($($tail)*)
};
() => {}
}
This behaves as expected on some inputs using rustc 1.66.0 (69f9c33d7 2022-12-12):
show_me!(x + 1);
# x is an ident and then I see + 1
# + is a mystery and then I see 1
# 1 is a literal and then I see
But if I try a negative number, I get (a little) surprised:
show_me!(-4);
# -4 is a literal and then I see
This would make perfect sense, except that https://doc.rust-lang.org/reference/procedural-macros.html says (under the description of declarative macros):
Literals ("string", 1, etc)
Note that negation (e.g. -1) is never a part of such literal tokens, but a separate operator token.
Where it gets weird to me is if I try to run show_me!(-x)
:
error: unexpected token: `x`
--> src/main.rs:54:15
|
54 | show_me!(-x);
| ^
|
::: src/utils/macros.rs:27:6
|
27 | ($l : literal $($tail : tt)*) => {
| ------------ while parsing argument for this `literal` macro fragment
Note that if I comment out the literal
arm of show_me
, or just move it to the bottom of the matcher, the other arms are perfectly happy with this:
show_me!(-x);
# - is a - and then I see x
# x is an ident and then I see
My expectation would be that the matching arm would either match successfully, or fall through to the next pattern. Instead, it's throwing compile errors. What is happening in this case?
Also, am I misreading the documentation or is it incorrect? To me it seems to clearly say that -
should never be part of literal expressions in declarative macros (though it may be part of procedural ones.)
This was asked in issue #82968, and the response was:
This is working as intended, non-terminal metavariables always fully commit to parsing a part of the syntax, if this fails, the whole macro call is rejected.
You can see the same behavior with e.g. expr
.