Description
The problem
In macros-by-example we have $crate
to refer to the crate the macro is defined in. This is very useful as the library author doesn't have to assume anything about how that crate is used in the user's crate (in particular, the user can rename the crate without breaking the world).
In the new proc macro system we don't seem to have this ability. It's important to note that just $crate
won't be useful most of the time though, because right now most crates using proc macros are structured like that:
foo-{macros/derive/codegen}
: this crate isproc-macro = true
and defines the actual proc macro.foo
: defines all runtime dependency stuff, hasfoo-{macros/derive/codegen}
as dependency and reexports the proc macro.- The important part: the proc macro emits code that uses stuff from
foo
An example:
foo-macros/src/lib.rs
#[proc_macro]
pub fn mac(_: TokenStream) -> TokenStream {
quote! { ::foo::do_the_thing(); }
}
foo/src/lib.rs
pub fn do_the_thing() {
println!("hello!");
}
When the user uses mac!()
now, they have to have do_the_thing
in scope, otherwise an error from inside the macro will occur. Not nice. Even worse: if the user has a do_the_thing
in scope that is not from foo
, strange things could happen.
So an equivalent of $crate
would refer to the foo-{macros/derive/codegen}
crate which is not all that useful, because we mostly want to refer to foo
. The best way to solve this right now is to use absolute paths everywhere and hope that the user doesn't rename the crate foo
to something else.
The proc macro needs to be defined in a separate crate and the main crate foo
wants to reexport the macro. That means that foo-macros
doesn't know anything about foo
and thus blindly emits code (tokens) hoping that the crate foo
is in scope.
But this doesn't sound like a very robust solution.
Furthermore, using the macro in foo
itself (usually for testing) is not trivial. The macro assumes foo
is an extern crate that can be referred to with ::foo
. But that's not the case for foo
itself. In one of my codebases I used a hacky solution: when the first token of the macro invocation is *
, I emit paths starting with crate::
instead of ::foo::
. But again, a better solution would be really appreciated.
How can we do better?
I'm really not sure, but I hope we can use this issue as place for discussion (I hope I didn't miss any previous discussion on IRLO).
However, I have one idea: declaring dependencies of emitted code. One could add another kind of dependencies (apart from dependencies
, dev-dependencies
and build-dependencies
) that defines what crates the emitted code depends on. (Let's call them emit-dependencies
for now, although that name should probably be changed.) So those dependencies wouldn't be checked/downloaded/compiled when the proc macro crate is compiled, but the compiler could make sure that those dependencies are present in the crate using the proc macro.
I guess defining those dependencies globally crate is not sufficient since different proc macros could emit code with different dependencies. So maybe we could define the emit-dependencies
per proc macro. But I'm not sure if that makes the check too complicated (because then Cargo would have to check which proc macros the user actually uses to collect a set of emit-dependencies
).
That's just one idea I wanted to throw out there.