The hygiene when nesting proc-macro in a declarative macro

I'm not sure whether I understand the hygiene of declarative macro correctly. Consider this case:

#[proc_macro]
pub fn gen_format(v:TokenStream)->TokenStream{
     let v = v.to_string();
     let s = format!(r##"format!("{{}}",{})"##,v);
     s.parse().unwrap()
}

Then, use it in a declarative macro, which looks something like this:

macro_rules! gen_help{
     ($($t:tt)*)=>{
         gen_format!($($t)*)
     }
}

Then, use the declarative macro

fn main(){
   let name = "abc";  // #1
   let r = gen_format!(name);
}

According to the hygiene specified in Hygiene - The Little Book of Rust Macros, the identifier is supplied in the argument of gen_format, so, the identifier name at #1 should be seen. However, the program just says

cannot find value name in this scope

What's the reason here? It appears to me the above example can be simplified to the following:

macro_rules! test_hygien {
	($($t:tt)*) => {
		format!("{}",$($t)*)
	};
}
fn main(){
	let name = "abc";
	let r = test_hygien!(name);
}

However, this code is OK.

I suspect this is because of this line:

let v = v.to_string();

Identifier hygiene is tracked by tacking invisible metadata on to identifiers. This invisible metadata does not survive an identifier being turned into a plain string. You can actually see this if you take code using macros that rely on hygiene, run it through cargo expand, then try to compile the result: the hygiene metadata will be gone and the code might no longer compile.

Basically: don't just blindly turn stuff into strings. I don't know how to do that without using syn as an intermediary.

It's very simple if tedious. Create a TokenStream, then extend it with a new indent or a group, rinse and repeat.

You don't have many kinds of entities at the proc_macro level, they are all created by syn, at proc_macro level there are are only Group, Ident, Punct, and Literal.

Here format would be Ident, ! and , are Puncts, "{}" is a Literal and you already have a TokenStream to put in Group. Easy if tedious, as I've said.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.