Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested macro_rules utilising inner metavariable are infinitely indented with repeated formatting #4609

Open
the6p4c opened this issue Dec 22, 2020 · 6 comments · May be fixed by #5473
Open
Labels
a-macros bug Panic, non-idempotency, invalid code, etc. help wanted

Comments

@the6p4c
Copy link

the6p4c commented Dec 22, 2020

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
@davidBar-On
Copy link
Contributor

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.

@the6p4c
Copy link
Author

the6p4c commented Jan 7, 2021

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

@davidBar-On
Copy link
Contributor

davidBar-On commented Jan 7, 2021

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).

@chris-morgan
Copy link
Member

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.

davidBar-On added a commit to davidBar-On/rustfmt that referenced this issue Jul 27, 2022
@davidBar-On
Copy link
Contributor

Submitted PR #5473 with a proposed solution.

@B1Z0N
Copy link

B1Z0N commented Jan 1, 2023

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

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-macros bug Panic, non-idempotency, invalid code, etc. help wanted
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants