There is this crate called async-coap-uri
, which exports a macro called uri
.
The macro works like this
use async_coap_uri::uri;
// verifies the uri at compile-time
let uri = uri!("https://www.example.com/");
The above code example is not working, because the uri
macro is defined like this:
#[macro_export]
macro_rules! uri {
( unsafe $S:expr ) => {{
// We don't do any correctness checks when $S is preceded by `unsafe`.
$crate::_uri_const!($S, $crate::Uri)
}};
( $S:expr ) => {{
assert_uri_literal!($S);
$crate::_uri_const!($S, $crate::Uri)
}};
( ) => {
$crate::uri!("")
};
}
and there is this assert_uri_literal
macro used that is not in scope:
error: cannot find macro `assert_uri_literal` in this scope
--> src/main.rs:4:15
|
4 | let uri = uri!("https://www.example.com/");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
error: could not compile `uri`.
To learn more, run the command again with --verbose
This macro is an implementation detail that is not supposed to be imported by the crate-user, so I decided to solve this problem (or so I thought...).
I thought I could simply solve the problem by using an absolute path in the macro
#[macro_export]
macro_rules! uri {
( unsafe $S:expr ) => {{
// We don't do any correctness checks when $S is preceded by `unsafe`.
$crate::_uri_const!($S, $crate::Uri)
}};
( $S:expr ) => {{
$crate::assert_uri_literal!($S);
$crate::_uri_const!($S, $crate::Uri)
}};
( ) => {
$crate::uri!("")
};
}
but this causes another compiler error
error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
--> async-coap-uri/src/macros.rs:205:9
|
205 | $crate::assert_uri_literal!($S);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: async-coap-uri/src/uri_type.rs:232:13
|
232 | uri!("//example.com/foo/bar").uri_type(),
| ----------------------------- in this macro invocation
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
note: the macro is defined here
--> async-coap-uri/src/lib.rs:237:1
|
237 | #[proc_macro_hack]
| ^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
the definition of assert_uri_literal
in lib.rs
:
// ...
use proc_macro_hack::proc_macro_hack;
/// Used by the `uri` macro to verify correctness at compile-time.
#[doc(hidden)]
#[proc_macro_hack]
pub use async_coap_uri_macros::assert_uri_literal;
// ...
Because I have no clue how to solve this I decided to check the mentioned issue (this was not an issue, but a PR and I had to scroll a bit to find the first relevant comment):
@CAD97
The error was introduced in dd0a766 to fix #53144 (and made a deprecation lint later to mitigate some fallout.The issue is that any
#[macro_export]
macrom
is placed into the root module, so the result of the query "ism
defined in the root?" is indeterminate until the crate is completely expanded and no potential#[macro_export]
s remain.Indeterminacies like this cause import resolution to stuck, like it happened in #53144 and similar cases.
With themacro_expanded_macro_exports_accessed_by_absolute_paths
error in place we can always give a determinate answer "yes" or "no" and prevent resolution from being stuck.
(More blunt, and therefore non-viable, alternative would be prohibiting macro-expanded#[macro_export]
s completely.)Perhaps the error can be relaxed a bit / made more fine-grained, but I haven't thought how to do that in detail.
I can agree with this decision, but this does not solve my problem.
After a bit of googling I found the edition guide (Redirecting...), where an entire section explains the new macro behavior and there is also mentioned
[...] will give an error message about not finding the
__impl_log!
macro. This is because unlike in the 2015 edition, macros are namespaced and we must import them. We could douse log::{__impl_log, error};
which would make our code compile, but
__impl_log
is meant to be an implementation detail!
It goes further on and explains that one should use
The cleanest way to handle this situation is to use the
$crate::
prefix for macros, the same as you would for any other path. Versions of the compiler >= 1.30 will handle this in both editions:
So this means the solution that I am supposed to use is not allowed? What should be done instead?
One solution would be to put another crate in between async-coap-uri
and async-coap-uri-macros
:
// and this one re-exports it (with proc_macro_hack)
async-coap-uri v0.1.0 (/media/hdd/home/projects/rust-async-coap/async-coap-uri)
│ // this crate defines the `assert_uri_literal` macro
└── async-coap-uri-macros v0.1.0 (/media/hdd/home/projects/rust-async-coap/async-coap-uri/proc-macros)
└── proc-macro-hack v0.5.14
// and this one would work :)
async-coap-uri v0.1.0 (/media/hdd/home/projects/rust-async-coap/async-coap-uri)
│ // and this one re-exports it (with proc_macro_hack)
└── async-coap-uri-macros-macros
│ // this crate defines the `assert_uri_literal` macro
└── async-coap-uri-macros v0.1.0 (/media/hdd/home/projects/rust-async-coap/async-coap-uri/proc-macros)
└── proc-macro-hack v0.5.14
but I am hoping that there is a better solution or at least an issue/rfc that tries to solve this problem.