rustfmt icon indicating copy to clipboard operation
rustfmt copied to clipboard

Formatting does not work with match arm having mixed OR clauses and comments

Open snprajwal opened this issue 9 months ago • 4 comments

Related to #6060 and #6044. For the below snippet, rustfmt does nothing at all:

enum Foo {
    A,
    B,
    C,
    D,
}

fn main() {
    let foo = Foo::A;
    match foo {
        // Here is a comment
Foo::A |
    // And another one
        Foo::B |
        // Hey look! There's another one
    Foo::C |
// That's a lot of comments
        Foo::D => {}
    }
}

If someone more knowledgeable about rustfmt internals can please confirm that this is a solvable bug, I'd be happy to contribute the fix.

snprajwal avatar Mar 04 '25 12:03 snprajwal

When you try formatting this with error_on_unformatted=true configured, you'll get the following error message:

error[internal]: not formatted because a comment would be lost
  --> <stdin>:10
   |
10 |     match foo {
   |
   = note: set `error_on_unformatted = false` to suppress the warning against comments or string literals

rustfmt isn't expecting to find comments within the OR pattern and would remove them if the code was formatted.

I'm not sure how simple this would be to solve. We usually use itemize_list to handle formatting lists of things internally, which takes care of rewriting comments as well.

It might be as simple as defining items (line 100) using itemize_list instead of mapping over the pat_strs, but I haven't looked into this.

https://github.com/rust-lang/rustfmt/blob/c6c8159bbc0a34b921429a57bfaffa7202468625/src/patterns.rs#L90-L117

It's also likely that any change here would need to be gated.

If you're interested in working on this one you can comment @rustbot claim to assign yourself.

ytmimi avatar Mar 04 '25 14:03 ytmimi

@rustbot claim

snprajwal avatar Mar 04 '25 16:03 snprajwal

I've played around with this in a few ways. The parser treats the first comment as a standalone comment, and the subsequent ones as "post-comments" for the preceding pattern. Using itemize_list() also does this, which means that a comment is joined into the same line as it's preceding pattern:

fn main() {
    let foo = Foo::A;
    match foo {
        // Here is a comment
        Foo::A // And another one
        | Foo::B // Hey look! There's another one
        | Foo::C // That's a lot of comments
        | Foo::D => {}
    }
}

This breaks pretty badly when there is a multi-line comment, or just multiple single line comments. Tbh, I'm not even sure what the expected formatting would be, it can vary based on the placement of the | separator. I can't think of a good way forward to fix this. Maybe it's best to leave it as an unformattable instance?

snprajwal avatar Mar 18 '25 20:03 snprajwal

@rustbot release-assignment

snprajwal avatar Mar 18 '25 20:03 snprajwal