rustfmt icon indicating copy to clipboard operation
rustfmt copied to clipboard

Nested `macro_rules` utilising inner metavariable are infinitely indented with repeated formatting

Open the6p4c opened this issue 4 years ago • 5 comments

Input

macro_rules! outer {
    ($d:tt) => {
        macro_rules! inner {
            ($d s:expr) => {
                println!("{}", $d s);
            }
        }
    };
}

outer!($);

fn main() {
    inner!("hi");
}

Output Note that the body and trailing closing braces of the nested macro_rules block is indented. With further invocations of rustfmt, this block is continuously indented to the next level.

Removing the usage of the inner macro_rules's $s in the println! usage causes the formatting to behave as expected.

macro_rules! outer {
    ($d:tt) => {
        macro_rules! inner {
                    ($d s:expr) => {
                        println!("{}", $d s);
                    }
                }
    };
}

outer!($);

fn main() {
    inner!("hi");
}

Expected output The input should likely remain unchanged.

Meta

  • rustfmt version: 1.4.27-nightly (2020-11-16 580d826), as used on Rust Playground
  • From where did you install rustfmt?: Rust Playground

the6p4c avatar Dec 22 '20 08:12 the6p4c

The issue is caused because original code snippet is used, since the macro code cannot be parsed properly: a , is missing after the $d in println!("{}", $d s). Submitted PR #4629 with a proposed solution to the issue.

davidBar-On avatar Jan 07 '21 17:01 davidBar-On

The original code does compile and execute as expected - I’m not sure what you mean by “cannot be parsed properly”.

the6p4c avatar Jan 07 '21 17:01 the6p4c

Oops! You are right. It is a while since I evaluated the root case .... Sorry for that. The real issue is that rusfmt doesn't know to distinguish between a $ parameter and other parameters (see issue #8), so it expects that there will be a , after the $d. This is the reason for using the original code snippet.

Note that the proposed fix is handling the general issue of formatting macro body when original code snippet is used, and not the specific issue of handling the $ items (which seem to require significant refactoring of the code).

davidBar-On avatar Jan 07 '21 17:01 davidBar-On

Another example:

macro_rules! alpha {
    () => {
        macro_rules! beta {
            () => {
                gamma!(*)
            };
        }
    };
}

Where I’ve written *, you look to be able to put anything that doesn’t parse as an expression.

I found a hint at a likely underlying problem: if you take just the inner macro_rules!, you get a case where it just gives up and leaves indentation as it is for lines 2–5, so that this is a stable wonky indentation:

macro_rules! delta {
 () => {
  epsilon!(*)
   };
    }

Whereas if you remove that * or replace it with something that can be parsed as an expression, it formats to what you’d expect.

chris-morgan avatar Sep 15 '21 09:09 chris-morgan

Submitted PR #5473 with a proposed solution.

davidBar-On avatar Jul 27 '22 18:07 davidBar-On

In case someone wants a quick fix for that and they are still not aware of rustfmt:skip.

image

B1Z0N avatar Jan 01 '23 01:01 B1Z0N