Description
When an attribute macro input contains $ident
, it is correctly substituted both inside and outside of a function-like macro invocation i.e. both of the $ident's in [u8; $ident + m!($ident)]
would be substituted as expected.
But when an attribute macro input contains $crate
, it is only substituted outside of a function-like macro. Inside, it incorrectly disintegrates into Punct('$'), Ident(crate)
when the whole bang macro invocation is passed to an attribute macro (or derive macro). In the case of [u8; $crate::N + m!($crate::N)]
the first $crate would be handled correctly but the second one would be passed incorrectly as two tokens. See where I put // WRONG
in the output below.
Cargo.toml
[package]
name = "repro"
version = "0.0.0"
edition = "2018"
publish = false
[lib]
proc-macro = true
src/main.rs
#[macro_export]
macro_rules! repro {
($ident:tt) => {
#[repro::repro]
type T = [$crate::$ident; x!($crate::$ident)];
};
}
repro!(N);
fn main() {}
src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn repro(_args: TokenStream, input: TokenStream) -> TokenStream {
println!("{:#?}", input);
TokenStream::new()
}
Output of cargo check:
TokenStream [
Ident {
ident: "type",
span: #0 bytes(0..0),
},
Ident {
ident: "T",
span: #0 bytes(0..0),
},
Punct {
ch: '=',
spacing: Alone,
span: #0 bytes(0..0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "crate",
span: #0 bytes(0..0),
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0),
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0),
},
Ident {
ident: "N",
span: #0 bytes(0..0),
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0),
},
Ident {
ident: "x",
span: #0 bytes(0..0),
},
Punct {
ch: '!',
spacing: Alone,
span: #0 bytes(0..0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Punct { // WRONG
ch: '$',
spacing: Alone,
span: #0 bytes(0..0),
},
Ident {
ident: "crate",
span: #0 bytes(0..0),
},
Punct {
ch: ':',
spacing: Joint,
span: #0 bytes(0..0),
},
Punct {
ch: ':',
spacing: Alone,
span: #0 bytes(0..0),
},
Ident {
ident: "N",
span: #0 bytes(0..0),
},
],
span: #0 bytes(0..0),
},
],
span: #0 bytes(0..0),
},
Punct {
ch: ';',
spacing: Alone,
span: #0 bytes(0..0),
},
]
Mentioning @petrochenkov who fixed my previous three $crate woes (#57089, #56622, #38016).