One other thing to note is, to verify the spans, you can also run the below
// Inside proc macro crate
use proc_macro::TokenStream;
#[proc_macro]
pub fn just_ret_let(v: TokenStream) -> TokenStream {
let ident = v.to_string();
let ts: proc_macro2::TokenStream = format!(r#"let {ident} = "just_ret_let";"#).parse().unwrap();
for token in ts.clone().into_iter() {
println!(
"Token: {:?}, Span Start: {:?}, Span End: {:?}, Source File: {:?}",
token,
token.span().start(),
token.span().end(),
token.span().source_file(),
);
}
ts.into()
}
// `main.rs` Inside main crate
macro_rules! test_me {
($name:ident) => {
proc_macro_test::just_ret_let!($name);
};
}
fn main() {
test_me!(ddd);
// Commented out since this will not compile
// println!("{ddd}");
}
with the command (using nightly features and configurations to obtain the span locations and source file),
RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo +nightly run
This will give an output like
Token: Ident { sym: let, span: #6 bytes(54..91) }, Span Start: LineColumn { line: 3, column: 8 }, Span End: LineColumn { line: 3, column: 45 }, Source File: SourceFile { path: "main/src/main.rs", is_real: true }
...
which in this case illustrates that the span of the tokens in ts
resolve to line 3 in main.rs
which is inside the test_me!
macro. This means the ddd
identifier is not in scope in main()
due to macro hygiene.
You can then run a similar command with the alternative suggestion to see the spans resolve inside the main()
function scope.