Opaque idents using proc-macro2 or the like

Hi folks,

I have a procedural macro that wraps some user-provided code in a function that accepts a parameter. I'd like to name the parameter something such that it is impossible for the user to access that parameter. Is such a thing possible?

E.g. I want foo!(x += 1) to turn into |context| x += 1 without the possibility of the user-provided code accessing context. (Obviously, this example is contrived, and in the real world, I do things with context). In addition, I'd like context to not shadow any of the user's variables and vice versa, the user's callback should not be able to shadow context.

I could also do let ident = Ident::new(format!("some_variable{}", random_number), Span::call_site()), but that seems like a hack. Alternatively, I could also do Ident::new("await" /* or another reserved word */, Span::call_site()) :joy:

Is there any way to do this?

This is called hygiene:

  • If you compile with the RUSTFLAGS='--cfg procmacro2_semver_exempt' environment variable, i.e., with the --cfg procmacro2_semver_exempt rust flag, then you can use experimental features of ::proc_macro2, among which there is Span's def_site() constructor, which contrary to call_site(), uses hygiene inherent to the macro itself rather than the hygiene whence the macro is called.

    • The user provided "body of the closure", by virtue of being provided by the user, uses hygiene whence the macro is called, and is thus effectively unable to interact with a context identifier that uses def_site() hygiene.

  • Given that the above technique requires an experimental proc-macro2 feature (the hygiene of procedural macros is not as polished as that of macro_rules! macros), you can instead make your procedural macro expand to a call to a macro_rules! macro, since the latter has def_site() hygiene for all its hard-coded identifiers:

  • The other option is to use an obfuscated identifier as you suggested: even if it is ugly, it works surprisingly well.

Thank you! That is a very helpful explanation and exactly the info I needed :slight_smile: