rust-lang / rustfmt

Format Rust code
https://rust-lang.github.io/rustfmt/
Apache License 2.0
6.06k stars 892 forks source link

Incorrectly strips r# prefix from labels #6411

Open Raspberry1111 opened 1 day ago

Raspberry1111 commented 1 day ago

Rust playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=051766b3584e787d3abab1d2f18424bb

The following code:

'r#if: {
  break 'r#if;
}

when run through either stable or nightly rustfmt will incorrectly be formatted into this:

'r#if: {
  break 'if;
}

which rustc will complain because 'if is an invalid label name:

error: invalid label name `'if`
 --> src/main.rs:5:15
  |
5 |         break 'if;
  |               ^^^

Note that this still does occur if the label name is not a keyword:

'r#a: {
  break 'r#a;
}

is formatted into

'r#a: {
  break 'a;
}

However this does not cause an error.

Versions of rustfmt (from rust-playground) 1.8.0-stable (2024-11-26 90b35a6239) and 1.8.0-nightly (2024-11-30 7442931d49)

ytmimi commented 1 day ago

@Raspberry1111 thanks for the report. Has it always been possible to use r# in labels?

Raspberry1111 commented 1 day ago

Judging from this pull request: https://github.com/rust-lang/rust/pull/126452 they were added in version 1.83 which is coincidentally the current stable release

Testing this with rustc +1.82 --edition 2021 test.rs gives:

error: expected `while`, `for`, `loop` or `{` after a label
 --> test.rs:4:7
  |
4 |     'r#if: {
  |       ^ expected `while`, `for`, `loop` or `{` after a label
  |
help: add `'` to close the char literal
  |
4 |     'r'#if: {
  |       +

error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `#`
 --> test.rs:4:7
  |
4 |     'r#if: {
  |       ^ expected one of 9 possible tokens

error: expected `while`, `for`, `loop` or `{` after a label
  --> test.rs:10:7
   |
10 |     'r#a: {
   |       ^ expected `while`, `for`, `loop` or `{` after a label
   |
help: add `'` to close the char literal
   |
10 |     'r'#a: {
   |       +

error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `#`
  --> test.rs:10:7
   |
10 |     'r#a: {
   |       ^ expected one of 9 possible tokens

error: aborting due to 4 previous errors

Running rustc +1.83 --edition 2021 test.rs produces no errors

ytmimi commented 1 day ago

Thanks for the extra info. Yeah, I can reproduce this using the latest commit 9f8fcc2f3e7c7ff48412c30cfda67306d8e3320f, and running rustfmt from source with cargo run --bin rustfmt -- --edition=2021:

input:

fn broken() {
    'r#if: {
        break 'r#if;
    }
}

fn works() {
    'r#a: {
        break 'r#a;
    }
}

output:

fn broken() {
    'r#if: {
        break 'if;
    }
}

fn works() {
    'r#a: {
        break 'a;
    }
}

Looks like break and continue formatting need to be updated to use the span of the Label, and not just the identifier:

https://github.com/rust-lang/rustfmt/blob/9f8fcc2f3e7c7ff48412c30cfda67306d8e3320f/src/expr.rs#L208-L226