I am creating an attribute macro that can be invoked with (a path to) some type amongst its attributes:
#[my_macro(path::to::SomeType)]
This macro generates a module, within which the specified type is to be used:
mod generated {
use path::to::SomeType;
}
However, from within that generated module, the path as provided may no longer be correct:
- if the path is prefixed with
::
, it remains correct—I can determine this within the proc macro; - otherwise, if the path starts with an identifier that is in a prelude (such as that of an external crate), it remains correct—I can't see any way to determine this within the proc macro;
- otherwise it must now be prefixed with
super::
for resolution to succeed.
Of course, I could require users of the macro to provide a path that will be correct from within the generated module, but that would not be very ergonomic.
Instead, I'm currently generating a type alias within the parent module:
type __generated_SomeType = path::to::SomeType;
mod generated {
use super::__generated_SomeType as SomeType;
}
However this is not only a tad clunky, but it furthermore pollutes the parent namespace.
Is there a better way?
I don't think there's a satisfying solution. But there's an alternative, and a way to improve your current approach.
The alternative is to use a
const
item:It can still cause name collisions with items from the parent scope, but at least the generated items aren't visible outside of the
const
. This is what most macro authors do.If you don't want to do that, you can go with your current approach, but you should add
#[doc(hidden)]
to the__generated_SomeType
type, so it doesn't show up in documentation. Also, you should use an import instead of a type alias to support generic types, as suggested by @ChayimFriedman:I was just about to suggest combining these to approaches by putting a module inside a
const
item, but it doesn't work:Rust complains that
__generated_SomeType
doesn't exist in the parent module.