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 is proc-macro = true and defines the actual proc macro.
foo: defines all runtime dependency stuff, has foo-{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:
Details
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.
Related
The problem
In macros-by-example we have
$crateto 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
$cratewon'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 = trueand defines the actual proc macro.foo: defines all runtime dependency stuff, hasfoo-{macros/derive/codegen}as dependency and reexports the proc macro.fooAn example:
Details
foo-macros/src/lib.rsfoo/src/lib.rsWhen the user uses
mac!()now, they have to havedo_the_thingin scope, otherwise an error from inside the macro will occur. Not nice. Even worse: if the user has ado_the_thingin scope that is not fromfoo, strange things could happen.So an equivalent of
$cratewould refer to thefoo-{macros/derive/codegen}crate which is not all that useful, because we mostly want to refer tofoo. The best way to solve this right now is to use absolute paths everywhere and hope that the user doesn't rename the cratefooto something else.The proc macro needs to be defined in a separate crate and the main crate
foowants to reexport the macro. That means thatfoo-macrosdoesn't know anything aboutfooand thus blindly emits code (tokens) hoping that the cratefoois in scope.But this doesn't sound like a very robust solution.
Furthermore, using the macro in
fooitself (usually for testing) is not trivial. The macro assumesfoois an extern crate that can be referred to with::foo. But that's not the case forfooitself. In one of my codebases I used a hacky solution: when the first token of the macro invocation is*, I emit paths starting withcrate::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-dependenciesandbuild-dependencies) that defines what crates the emitted code depends on. (Let's call thememit-dependenciesfor 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-dependenciesper 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 ofemit-dependencies).That's just one idea I wanted to throw out there.
Related
serdeissue related to this$cratein custom derive paths dtolnay/syn#507extern_crate_self#56409