From 17dd04537317cf987a30deba99ef96548ae8b871 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 9 Aug 2026 03:25:10 +0200 Subject: [PATCH] Suggest adding a body for a match arm followed by a comma Only suggest a tuple or an or-pattern when the comma-separated patterns are followed by a `=>` or a guard. Otherwise the arm is just missing a body, which `parse_arm` already reports. --- .../rustc_parse/src/parser/diagnostics.rs | 14 +++++ compiler/rustc_parse/src/parser/pat.rs | 2 + .../feature-gate-never_patterns.rs | 3 +- .../feature-gate-never_patterns.stderr | 54 ++++++++++++------- tests/ui/parser/match-arm-without-body.rs | 10 +++- tests/ui/parser/match-arm-without-body.stderr | 45 ++++++++++------ 6 files changed, 91 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f63064002c559..96caf0b8fd706 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2929,12 +2929,26 @@ impl<'a> Parser<'a> { // parentheses in what should have been a tuple pattern; return a // suggestion-enhanced error here rather than choking on the comma later. let comma_span = self.token.span; + // `EitherTupleOrPipe` is only ever used for the pattern of a `match` arm, which has to be + // followed by either a `=>` or an `if` guard. Take a snapshot so that we can rewind to the + // comma if neither of those follows the comma-separated list. + let snapshot = (rt == CommaRecoveryMode::EitherTupleOrPipe) + .then(|| self.create_snapshot_for_diagnostic()); self.bump(); if let Err(err) = self.skip_pat_list() { // We didn't expect this to work anyway; we just wanted to advance to the // end of the comma-sequence so we know the span to suggest parenthesizing. err.cancel(); } + if let Some(snapshot) = snapshot + && self.token != token::FatArrow + && !self.token.is_keyword(kw::If) + { + // Neither parenthesising the patterns nor joining them with `|` would give the arm a + // body, so the comma is far more likely to stand in for a missing `=> `. + self.restore_snapshot(snapshot); + return Ok(()); + } let seq_span = lo.to(self.prev_token.span); let mut err = self.dcx().struct_span_err(comma_span, "unexpected `,` in pattern"); err.multipart_suggestion( diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 026fe8acb35cd..08e37c9d4c36b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -73,6 +73,8 @@ pub enum RecoverColon { #[derive(PartialEq, Copy, Clone)] pub enum CommaRecoveryMode { LikelyTuple, + /// Only used for the pattern of a `match` arm, which the recovery relies on: it gives up when + /// the comma-separated list is not followed by the `=>` or the guard of an arm. EitherTupleOrPipe, } diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs index 2cb0b5a6679d5..cb7155a8ad8af 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs @@ -31,7 +31,7 @@ fn main() { match Some(0) { None => {} Some(_), - //~^ ERROR unexpected `,` in pattern + //~^ ERROR `match` arm with no body } match Some(0) { None => {} @@ -59,6 +59,7 @@ fn main() { // Check that the gate operates even behind `cfg`. match Some(0) { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered None => {} #[cfg(false)] Some(_) diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index e655209924a08..ad0057e86e695 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -1,19 +1,3 @@ -error: unexpected `,` in pattern - --> $DIR/feature-gate-never_patterns.rs:33:16 - | -LL | Some(_), - | ^ - | -help: try adding parentheses to match on a tuple... - | -LL | (Some(_),) - | + + -help: ...or a vertical bar to match on alternatives - | -LL - Some(_), -LL + Some(_) | - | - error[E0658]: `!` patterns are experimental --> $DIR/feature-gate-never_patterns.rs:8:24 | @@ -54,6 +38,17 @@ LL | ! => {} = help: add `#![feature(never_patterns)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error: `match` arm with no body + --> $DIR/feature-gate-never_patterns.rs:33:9 + | +LL | Some(_), + | ^^^^^^^ + | +help: add a body after the pattern + | +LL | Some(_) => { todo!() }, + | ++++++++++++++ + error: `match` arm with no body --> $DIR/feature-gate-never_patterns.rs:38:9 | @@ -108,7 +103,7 @@ LL | Err(!) if false, = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:64:9 + --> $DIR/feature-gate-never_patterns.rs:65:9 | LL | Some(_) | ^^^^^^^ @@ -119,7 +114,7 @@ LL | Some(_) => { todo!() } | ++++++++++++++ error: `match` arm with no body - --> $DIR/feature-gate-never_patterns.rs:70:9 + --> $DIR/feature-gate-never_patterns.rs:71:9 | LL | Some(_) if false | ^^^^^^^^^^^^^^^^ @@ -135,6 +130,25 @@ error: a guard on a never pattern will never be run LL | Err(!) if false, | ^^^^^ help: remove this guard -error: aborting due to 13 previous errors +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/feature-gate-never_patterns.rs:61:11 + | +LL | match Some(0) { + | ^^^^^^^ pattern `Some(_)` not covered + | +note: `Option` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + Some(_) => todo!() + | + +error: aborting due to 14 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0004, E0658. +For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs index 7fe5b6d253980..7711f7314496f 100644 --- a/tests/ui/parser/match-arm-without-body.rs +++ b/tests/ui/parser/match-arm-without-body.rs @@ -13,18 +13,26 @@ fn main() { _ => {} //~^ ERROR expected one of } + match Some(false) { + Some(_), + //~^ ERROR `match` arm with no body + //~| HELP add a body after the pattern + } + // The comma is only taken for a missing body when parenthesising the patterns (or joining + // them with `|`) wouldn't give the arm a body either. match Some(false) { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple //~| HELP or a vertical bar to match on alternative + _ => {} } match Some(false) { Some(_), //~^ ERROR unexpected `,` in pattern //~| HELP try adding parentheses to match on a tuple //~| HELP or a vertical bar to match on alternative - _ => {} + None if true => {} } match Some(false) { Some(_) if true diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index 59a323f2cc1cf..297875912047f 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -7,15 +7,19 @@ LL | _ => {} | ^ unexpected token error: unexpected `,` in pattern - --> $DIR/match-arm-without-body.rs:17:16 + --> $DIR/match-arm-without-body.rs:24:16 | LL | Some(_), | ^ | help: try adding parentheses to match on a tuple... | -LL | (Some(_),) - | + + +LL ~ (Some(_), +LL | +LL | +LL | +LL ~ _) => {} + | help: ...or a vertical bar to match on alternatives | LL - Some(_), @@ -23,7 +27,7 @@ LL + Some(_) | | error: unexpected `,` in pattern - --> $DIR/match-arm-without-body.rs:23:16 + --> $DIR/match-arm-without-body.rs:31:16 | LL | Some(_), | ^ @@ -34,7 +38,7 @@ LL ~ (Some(_), LL | LL | LL | -LL ~ _) => {} +LL ~ None) if true => {} | help: ...or a vertical bar to match on alternatives | @@ -43,7 +47,7 @@ LL + Some(_) | | error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_` - --> $DIR/match-arm-without-body.rs:36:9 + --> $DIR/match-arm-without-body.rs:44:9 | LL | Some(_) if true | - expected one of `.`, `=>`, `?`, or an operator @@ -51,7 +55,7 @@ LL | _ => {} | ^ unexpected token error: expected `,` following `match` arm - --> $DIR/match-arm-without-body.rs:66:15 + --> $DIR/match-arm-without-body.rs:74:15 | LL | pat!() | ^ @@ -73,7 +77,18 @@ LL | Some(_) => { todo!() } | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:30:9 + --> $DIR/match-arm-without-body.rs:17:9 + | +LL | Some(_), + | ^^^^^^^ + | +help: add a body after the pattern + | +LL | Some(_) => { todo!() }, + | ++++++++++++++ + +error: `match` arm with no body + --> $DIR/match-arm-without-body.rs:38:9 | LL | Some(_) if true | ^^^^^^^^^^^^^^^ @@ -84,7 +99,7 @@ LL | Some(_) if true => { todo!() } | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:40:9 + --> $DIR/match-arm-without-body.rs:48:9 | LL | Some(_) if true, | ^^^^^^^^^^^^^^^ @@ -95,7 +110,7 @@ LL | Some(_) if true => { todo!() }, | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:45:9 + --> $DIR/match-arm-without-body.rs:53:9 | LL | Some(_) if true, | ^^^^^^^^^^^^^^^ @@ -106,7 +121,7 @@ LL | Some(_) if true => { todo!() }, | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:51:9 + --> $DIR/match-arm-without-body.rs:59:9 | LL | pat!() | ^^^^^^ @@ -117,7 +132,7 @@ LL | pat!() => { todo!() } | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:56:9 + --> $DIR/match-arm-without-body.rs:64:9 | LL | pat!(), | ^^^^^^ @@ -128,7 +143,7 @@ LL | pat!() => { todo!() }, | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:61:9 + --> $DIR/match-arm-without-body.rs:69:9 | LL | pat!() if true, | ^^^^^^^^^^^^^^ @@ -139,7 +154,7 @@ LL | pat!() if true => { todo!() }, | ++++++++++++++ error: `match` arm with no body - --> $DIR/match-arm-without-body.rs:72:9 + --> $DIR/match-arm-without-body.rs:80:9 | LL | pat!(), | ^^^^^^ @@ -149,5 +164,5 @@ help: add a body after the pattern LL | pat!() => { todo!() }, | ++++++++++++++ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors