rust-lang / rust-lang/rustfmt

Match arm trailing whitespace triggering error; match arm lone comma before comment unformattable

Open
#5,537 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-comments A-matches I-trailing-whitespace
Dominant language
Rust
Stars
7k
Forks
1.1k
Avg merge
2d 13h
Merged PRs (30d)
24

Description

Hello, I am encountering a combination of two issues.

$ rustc --version
rustc 1.63.0 (4b91a6ea7 2022-08-08)
$ cargo fmt --version
rustfmt 1.5.1-stable (4b91a6e 2022-08-08)

To my understanding there is no config file in effect.

After a bit of research, I think this issue is tangentially related toe #5535, as both are somewhat related to unformattable code due to potential loss of comments.


The issues are demonstrated in the following snippet. Note that the first match arm has trailing comma on a separate line, and that the Err(()) expression on the second match arm has a few trailing spaces.

Snippet 1
enum A {
    A1,
    A2,
}

struct B { field : bool, unused: () }

fn main() {}

fn f(a: A, b: B) -> Result<(), ()> {
    match (a,b ) {
        // arm 1
        (A::A1, B { field: true, ..}) =>
            Ok(println!("A1 + B=T"))
,
        // arm 2
        _ => Err(()),    
    }
}

In this form, cargo fmt --check generates the following error regarding the space after Err(()), causing my IDE to not format the code at all:

$ cargo fmt --check
Diff in /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs at line 3:
     A2,
 }

-struct B { field : bool, unused: () }
+struct B {
+    field: bool,
+    unused: (),
+}

 fn main() {}

error[internal]: left behind trailing whitespace
  --> /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs:20:20:22
   |
20 |         _ => Err(()),
   |                      ^^^^^^^^
   |

warning: rustfmt has failed to format. See previous 1 errors.

As suggested by #5535, but I don't quite understand which comment would be lost and why.

$ cargo fmt --check -- --config=error_on_unformatted=true
Diff in /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs at line 3:
     A2,
 }

-struct B { field : bool, unused: () }
+struct B {
+    field: bool,
+    unused: (),
+}

 fn main() {}

error[internal]: not formatted because a comment would be lost
  --> /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs:11
   |
11 |     match (a,b ) {
   |
   = note: set `error_on_unformatted = false` to suppress the warning against comments or string literals

error[internal]: left behind trailing whitespace
  --> /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs:20:20:22
   |
20 |         _ => Err(()),
   |                      ^^^^
   |

warning: rustfmt has failed to format. See previous 2 errors.

After I remove the trailing spaces after Err(()) from snippet 1, the following is the formatted snippet.

Snippet 2
enum A {
    A1,
    A2,
}

struct B {
    field: bool,
    unused: (),
}

fn main() {}

fn f(a: A, b: B) -> Result<(), ()> {
    match (a,b ) {
        // arm 1
        (A::A1, B { field: true, ..}) =>
            Ok(println!("A1 + B=T"))
,
        // arm 2
        _ => Err(()),
    }
}

Note that the comma stays unchanged, whereas I expect it to be at the right of Ok(..). Even if I add spaces before the comma, it is never formatted to join the Ok(..) line. Also note that (a,b ) is not formatted.

Now as I format with error_on_unformatted:

$ cargo fmt --check -- --config=error_on_unformatted=true
error[internal]: not formatted because a comment would be lost
  --> /home/user/.cache/cargo-temp/tmp-cM6RJ2/src/main.rs:14
   |
14 |     match (a,b ) {
   |
   = note: set `error_on_unformatted = false` to suppress the warning against comments or string literals

warning: rustfmt has failed to format. See previous 1 errors.

When I join the comma with the previous line from snippet 2, the following is the formatted snippet.

Snippet 3
enum A {
    A1,
    A2,
}

struct B {
    field: bool,
    unused: (),
}

fn main() {}

fn f(a: A, b: B) -> Result<(), ()> {
    match (a, b) {
        // arm 1
        (A::A1, B { field: true, .. }) => Ok(println!("A1 + B=T")),
        // arm 2
        _ => Err(()),
    }
}

Notice that now the comma and Ok(..) are now joined together.


When I instead remove the "arm 2" comment from snippet 2, the following is the formatted snippet.

Snippet 4
enum A {
    A1,
    A2,
}

struct B {
    field: bool,
    unused: (),
}

fn main() {}

fn f(a: A, b: B) -> Result<(), ()> {
    match (a, b) {
        // arm 1
        (A::A1, B { field: true, .. }) => Ok(println!("A1 + B=T")),
        _ => Err(()),
    }
}

In this case we see again that Ok(..) are joined with the comma.


Edit: after a bit more of digging around the issues, I think #5521 might be related to the first issue I have, the trailing whitespaces.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the behavior with the four Rust snippets using cargo fmt --check and the error_on_unformatted configuration described in the report. Trace how match arms, comments, trailing whitespace, and a comma on its own line are handled; done means the snippets format without the reported internal errors while preserving comments and producing consistent arm formatting.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.