rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.99k stars 12.68k forks source link

Stray "match" can result in bad syntax error messages #114265

Open ijackson opened 1 year ago

ijackson commented 1 year ago

Code

fn main() {
    let mut iter = "hello".chars();
    let mut variable;

    while let Some(thing) = match iter.next() {
        let variable = thing;        
    }
}

Current output

error: expected pattern, found `let`
 --> src/main.rs:6:9
  |
6 |         let variable = thing;        
  |         ^^^ help: remove the unnecessary `let` keyword

error: expected one of `=>`, `@`, `if`, or `|`, found `=`
 --> src/main.rs:6:22
  |
6 |         let variable = thing;        
  |                      ^
  |                      |
  |                      expected one of `=>`, `@`, `if`, or `|`
  |                      help: try using a fat arrow here: `=>`

error: `match` arm body without braces
 --> src/main.rs:6:24
  |
6 |         let variable = thing;        
  |                      - ^^^^^ this statement is not surrounded by a body
  |                      |
  |                      while parsing the `match` arm starting here
  |
help: replace `;` with `,` to end a `match` arm expression
  |
6 |         let variable = thing,        
  |                             ~

error: expected `{`, found `}`
 --> src/main.rs:8:1
  |
5 |       while let Some(thing) = match iter.next() {
  |  _____-----_-
  | |     |
  | |     while parsing the body of this `while` expression
6 | |         let variable = thing;        
7 | |     }
  | |_____- this `while` condition successfully parsed
8 |   }
  |   ^ expected `{`

Desired output

error: expected pattern inside match statement, found `let`
 --> src/main.rs:6:9
  |
6 |         let variable = thing;        
  |         ^^^ help: remove the unnecessary `let` keyword

error: expected one of `=>`, `@`, `if`, or `|` (inside match statement), found `=`
 --> src/main.rs:6:22
  |
6 |         let variable = thing;        
  |                      ^
  |                      |
  |                      expected one of `=>`, `@`, `if`, or `|`
  |                      help: try using a fat arrow here: `=>`

note: within this `match` expression
 --> src/main.rs:6:9
  |
6 |     while let Some(thing) = match iter.next() {        
  |                             ^^^^^ note: match expression starts here

Rationale and extra context

When refactoring or reorganising, one often ends up inserting and removing match (replacing the match with an if let or some combinator like .map). If the match isn't deleted, the parser goes badly astray.

It would be nice if it revisited the assumption that the match was correct, but that may be hard. Easier is probably to just point to the match statement.

Other cases

Deleting the let as the compiler suggests produces this:

error: expected one of `=>`, `@`, `if`, or `|`, found `=`
 --> src/main.rs:6:18
  |
6 |         variable = thing;        
  |                  ^
  |                  |
  |                  expected one of `=>`, `@`, `if`, or `|`
  |                  help: try using a fat arrow here: `=>`

error: `match` arm body without braces
 --> src/main.rs:6:20
  |
6 |         variable = thing;        
  |                  - ^^^^^ this statement is not surrounded by a body
  |                  |
  |                  while parsing the `match` arm starting here
  |
help: replace `;` with `,` to end a `match` arm expression
  |
6 |         variable = thing,        
  |                         ~

Anything else?

No response

djc commented 1 year ago

Came here to file the same issue. I feel like I've been running into this relatively often, and the current diagnostics also look unhelpful when viewed in an IDE (in my case, VS Code):

Screenshot 2023-08-08 at 14 41 38

See how all the errant spans are pretty far away from the actual problem.

Refactoring from match to if let (or, I suppose, while let, as the OP suggests) is common and I've caught myself making this mistake a bunch of times.