Issue with #[macro_export] and duplicate macro naming

I'm working on a proc macro that generates macro_rules macros as part of its output. I need these macros to be accessible outside of the crate they're generated in. However, I know you can't do this:

mod mod_a {
    #[macro_export]
    macro_rules! foo {
        () => {}
    }
}

mod mod_b {
    #[macro_export]
    macro_rules! foo {
        () => {}
    }
}

My proc macro may be used in multiple different places in the same crate, and I'd like to allow that to happen if it's done in different submodules. I'm a little stuck on how to do that while still getting exportable macros.

One thing I could do is:

mod mod_a {
    #[macro_export]
    macro_rules! foo_1 {
        () => {}
    }
    pub use foo_1 as foo;
}

mod mod_b {
    #[macro_export]
    macro_rules! foo_2 {
        () => {}
    }
    pub use foo_2 as foo;
}

And then mod_a::foo and mod_b::foo would be usable. However, this requires the proc macro to assign a unique name to each macro. I could possibly do this by hashing the file path and row-column info from the Span::call_site(), but unfortunately that's unstable functionality (proc_macro_span). I could generate a UUID, but that would make it difficult to cache the macro output since it would be constantly changing the output irrespective of changes to the input.

Is there another way of accomplishing this goal of generating multiple macros with the same name, exported from the crate, but distinguished by their path?

Why do you need two macros with the same name? It's impossible to use macro export to do it, since it means you define the macro in the root mod twice which isn't allowed.

If you want a macro to bahave like normal items with visibility, you'll need the unstable decl_macro feature. Rust Playground

They don't necessarily need to be the same name, but these are macros that are generated from another macro, which may be called more than once in the same crate (albeit in different submodules).

I could give them unique names in that process, but I'd need a way to do so in a stable manner -- a UUID or something wouldn't work since it would change even if the macro never moves or changes input.

I'd also like to stick to stable features.

Path-Based Scope

By default, a macro_rules! macro has no path-based scope. However, if it has the #[macro_export] attribute, then it is declared in the crate root scope and can be referred to similar to how you refer to any other item. The Import and Export chapter goes more in-depth into said attribute.

The stable way is to generate different names with macro_use, and with no path-based scope (meaning macros are global in submodules). Rust Playground

  1. How would I generate a unique name for each output macro if they're both made from different invocations of the same proc macro (without asking the user to uniquely name them)? As I mentioned above, a UUID wouldn't be a good idea, and getting path information from the Span is still unstable.

  2. Doesn't #[macro_use] not work (easily) for exporting macros outside of the crate?

Use format_ident in quote - Rust to generate an ident. Rust Playground

Sure, I understand the mechanics -- I use format_ident! all the time. I'm looking for suggestions on what input data to use to give the macro a unique name. I can't use a hash of the source location (that's an unstable feature), and it probably isn't a good idea to use a UUID either due to that being somewhat hostile to caching. I also can't guarantee that the input data will be unique for different macro calls.

Did you read the playground link where unique macro names can be generated from

let macro_name = (0..mod_name.len()).map(|i| quote::format_ident!("foo{i}"));

How would the proc macro dynamically access the mod names? Is there a way to get the path of where a proc macro was invoked?

You're asking too many different questions at the same time. But sorry, I'm too tired to think and answer.

The issue I'm having isn't really mechanical. I know how to manipulate the identifier names of macros. This is a request for suggestions on what information I can use to generate good unique identifiers for those macros.

For example, if a user invokes my proc macro twice in their crate:

mod a {
    my_proc_macro!(input, data);
}

mod b {
    mod c {
        my_proc_macro!(input, data);
    }
}

And if in both cases, my_proc_macro!(input, data); resolves to (among other things):

#[macro_export]
macro_rules! some_child_macro {
    (...) => { ... }
}

I need a way to either bypass the issue that #[macro_export] moves all macros to the crate scope and causes collisions (while still ensuring that the macros are indeed exported out of the crate), or I need a way for the proc macro to somehow uniquely name each macro without requiring the user to feed in unique input.

As I mentioned above, I could generate a UUID, so I generate something like

#[macro_export]
macro_rules! some_child_macro_123e4567_e89b_12d3_a456_426614174000 {
    (...) => { ... }
}
pub use some_child_macro_123e4567_e89b_12d3_a456_426614174000 as some_child_macro;

That would mean that a::some_child_macro!() and b::c::some_child_macro!() would be uniquely accessible without collision.

But UUIDs aren't stable -- two different compilations of the same code would generate different UUIDs, which would harm compilation/analyzer times due to the inability to cache macros. Similarly, I could generate a hash from the callsite file path and line/column number, but the ability to access that information from a Span within a proc macro is unstable.

I'm asking to see if anyone has any suggestions for a way I could generate a stable, unique tag for an identifier generated by a proc macro without requiring nightly.

1 Like

I don't know the details of what is and isn't guaranteed about proc macro invocations (not much), but you might be able to get away with static and/or thread-local counters in place of a UUID. That would keep the identifiers relatively (but not completely) stable, as long as the compiler uses some deterministic algorithm to determine the order it expands proc macros.

...which is not guaranteed with incremental compilation. It's even not guaranteed that all proc macro invocations will be actually called.

1 Like

It's not perfect and some manual effort, but one approach is to take a hash of the input and use that to create the discriminator. The tokenstream types don't implement hash, but you can hash the .to_string(). It's what I did with macro_pub.

This doesn't solve multiple invocations with identical contents, but it does (probabilistically) handle the case where the invocations are different. If all else fails, just enable/require the macro consumer to provide a crate-unique identifier so you can just use that.

Also consider if you can get away with making the macro only pub(crate) and not exported, as then you can just avoid using #[macro_export] at all.

1 Like

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.