Braces required for single item import in macro but rustfmt removes them

In a macro that takes a module or path as an input, it seems that you're required to use it like this:

macro_rules! get_msg {
    ($module:path) => {
        let message = {
            use $module::{MESSAGE}; // note the (normally unnecessary) braces
            MESSAGE
        };
        println!("{}", message);
    };
}

This:

        let message = {
            use $module::MESSAGE; // note the missing braces
            MESSAGE
        };

and this:

        let message = $module::MESSAGE;

fail to compile.

However, rustfmt turns the first (working) example into the second example (without the braces). I couldn't find anything in these:

https://doc.rust-lang.org/book/ch19-06-macros.html
https://doc.rust-lang.org/rust-by-example/macros.html
https://doc.rust-lang.org/reference/macros.html

about that requirement, so I'm not sure if this is a known/intended limitation, or if there's a better way to do this.

Is this an issue with rust, rustfmt, both, or neither?

Playground link

1 Like

Definitely an issue with at least rustfmt; it should never change semantics or break code.

Here's a related issue, I'm not sure if it's the same issue though. You could ask there or open a new issue referencing the existing one as a possible dupe.

It's weird that {} are even required, so I guess rustfmt just isn't aware that this can happen. You can put #[rustfmt::skip] on the macro to keep rustfmt from breaking it.

Here's an alternative that rustfmt doesn't break. As a bonus, it also adds the ability to use associated constants.

macro_rules! get_msg {
    ($module:path) => {
        let message = {
            use $module as m;
            m::MESSAGE
        };
        println!("{}", message);
    };
}
3 Likes

Do you want to accept get_msg!(a::<T>::Q() -> Z)? If not, you don't want the $:path binder. Honestly, because $:path is a whole path, not a path fragment, it's rarely what you actually want. Despite being much more annoying to work with for the macro, you usually want $($m:ident)::+ instead of $m:path (except when you want $T:ty).

That this is even allowed with brackets is surprising.

4 Likes

That's a great clarification on path. I was definitely under the impression it was just SimplePath-- no clue where I got that from. I was able to refactor to just use a single ident in this case which did indirectly solve the issue.

I also opened this issue for the original problem.