"Random" identifiers in a decl macro

I'm playing around with making a decl macro to parse Rust syntax, for use by other decl macros. The idea is to strip away all the tedious, repetitive, and tricky bits when writing most Rust-parsing decl macros, and give the user a relatively clean interface.

To do this, I've got the following structure:

#[doc(hidden)]
pub use paste::paste;

#[macro_export]
macro_rules! parse_struct {
    ( $macro_name:ident, /* some arguments n' stuff */ ) => {
        $crate::paste! {
            // in reality, these `#[macro_export]`s are conditional;
            // the defined macros can be can be crate-private

            // this inner macro is necessary to achieve ergonomic results
            #[doc(hidden)]
            #[macro_export]
            macro_rules! [< __declrsyn_inner_ $macro_name >] {
                /* ... */
            }

            #[macro_export]
            macro_rules! $macro_name {
                (/* ... */) => {
                    /* some stuff involving the above-defined, private macro */
                };
            }
        }
    };
}

The problem is that the user could, in theory, define multiple macros within their crate with the same name, in different places. Due to how macros namespace, they would then conflict at the crate root.

I could write my own paste! replacement proc macro that generates a random identifier each time, but that would force a recompilation even when nothing has changed. I could try and hash the inputs instead, and use that, but that seems too fragile (and technically doesn't entirely solve the problem).

Is there a clean solution to this? Is there even a compelling reason to make this a decl macro instead of a proc macro, given that I already have to involve proc macros for generating identifiers?

I'm still interested in solutions, but I no longer will pursue this as a decl macro. Decl macros are actually incapable of parsing Rust's syntax (for example, in where bounds, the syntax allows TypePath + TypePath, but the path matcher doesn't allow being followed by +). So to be able to handle all valid Rust, you must use a proc macro.

@Kyllingene

  • What's the best way to create arbitrary identifiers for proc macros? (but only partially related)
  • yes, procedural macros give you more control (especially over Span, so you can control hygiene/unhygiene; and they are the only (stable) way to have a macro that generate other (useful, that is, parameterized) declarative macros - the only other way is through macros 2.0, which is nightly-only).

If your example is exactly what you need, then you can achieve it with a proc macro (assuming that the randomness source/entropy is good enough).

However, if there is a chance that your outer macro would be invoked with the same parameters/input from multiple consumer places (whether those places are within one crate, or multiple crates), then that would involve side effects/"state" between invocations of your proc macro. And, while (I believe) it's not explicitly documented anywhere, the proc macro API and cargo environment variables, limitations/docs on build.rs script etc., many comments/discussions and the existence of incremental compilation imply that storing/keeping a "state" between invocations of proc macros is a no-no. (It will cause problems, and ones that are not trivial to replicate.)