Understanting macro hygine 1.0

I understand most of how macro hygiene works in terms of identifiers defined within a macro, however, i don't quite understand how identifier resolution works.

this fails because it looks for an identifier in the calling crate:

const X: u8 = 1;

/// ```
/// use playground::m;
/// assert_eq!(m!(), X);
/// ```
#[macro_export]
macro_rules! m {
    () => (X);
}

but this seems to find a macro from the context of the defining crate:

use quote;
const X: u8 = 1;

/// ```
/// use playground::m;
/// let _ = m!();
/// ```
#[macro_export]
macro_rules! m {
    () => (quote::quote!(10));
}

but at the same time, if the two macros are defined in the same crate, you need to use $crate to refer to them, and they must be public.

what's going on here?

If this is the Rust playground there's a bunch of pre-installed crates, including quote:

Oh that's interesting.

So the first one will place the identifier X in the crate it is called from, which could lead to an error if no X is defined.

The second one is executing the quote! macro, and placing the result 10 in the calling crate.

So it would appear that consts are not "inlined" into a macro (as expected), but (proc?) macros are executed within a macro definition... If and only if they are imported by the crate defining the macro?.

What happens if you remove the use quote from the second example?

What happens if you use a macro rules macro?
I think it will run inside of the macro defining crate and its output will be placed into the calling crate, the same as a proc macro, if it's defined. And place the raw tokens if it is not, same as a proc macro.

Oh second point, does your first doctest need to define an X for the assertion?

What if it's instead:

/// ```
/// use playground::m;
/// assert_eq!(m!(), 1);
/// ```

No, not at all. The macro call to quote::quote! succeeded because the quote crate is in scope for your doctest code.

(If you're thinking "I didn't use quote; in the test" — the line use quote; has almost no effect, and the effect that it does have is irrelevant in this case.)

1 Like

even with rust2015[1] and disabling crate injection, it still works, so it doesn't seem to have anything to do with the extern prelude...

/// ```edition2015
/// #![doc(test(no_crate_inject))]
/// use playground::m;
/// let _ = m!();
/// ```
#[macro_export]
macro_rules! m {
    () => (quote::quote!(10));
}


  1. yes, i ran the whole thing with the 2015 edition ↩ī¸Ž

Oh duh, yeah I forgot that rust imports crates implicitly for you. Thanks for pointing that out!

not in rust 2015 though AFAIK, and this behavior is still present in the 2015 edition.

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.