Repository: rust-lang/rust-analyzer
Pull request #16885 (2024-03-19 09:57:18)

fix: Improve error recovery for match arms
This should make use of the recovery token sets, but I think it'd be better to fix that as a whole while fixing the other places for these adhoc recovery checks.


Patch:
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 72848a1f2b..54ed5f0ba2 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -490,6 +490,18 @@ fn match_expr(p: &mut Parser<'_>) -> CompletedMarker {
     m.complete(p, MATCH_EXPR)
 }
 
+// test_err match_arms_recovery
+// fn foo() {
+//     match () {
+//         _ => (),,
+//         _ => ,
+//         _ => (),
+//          => (),
+//         if true => (),
+//         _ => (),
+//         () if => (),
+//     }
+// }
 pub(crate) fn match_arm_list(p: &mut Parser<'_>) {
     assert!(p.at(T!['{']));
     let m = p.start();
@@ -511,6 +523,10 @@ pub(crate) fn match_arm_list(p: &mut Parser<'_>) {
             error_block(p, "expected match arm");
             continue;
         }
+        if p.at(T![,]) {
+            p.err_and_bump("expected pattern");
+            continue;
+        }
         match_arm(p);
     }
     p.expect(T!['}']);
@@ -544,26 +560,30 @@ fn match_arm(p: &mut Parser<'_>) {
     // }
     attributes::outer_attrs(p);
 
-    patterns::pattern_top_r(p, TokenSet::EMPTY);
+    patterns::pattern_top_r(p, TokenSet::new(&[T![=], T![if]]));
     if p.at(T![if]) {
         match_guard(p);
     }
     p.expect(T![=>]);
-    let blocklike = match expr_stmt(p, None) {
-        Some((_, blocklike)) => blocklike,
-        None => BlockLike::NotBlock,
-    };
-
-    // test match_arms_commas
-    // fn foo() {
-    //     match () {
-    //         _ => (),
-    //         _ => {}
-    //         _ => ()
-    //     }
-    // }
-    if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) {
-        p.error("expected `,`");
+    if p.eat(T![,]) {
+        p.error("expected expression");
+    } else {
+        let blocklike = match expr_stmt(p, None) {
+            Some((_, blocklike)) => blocklike,
+            None => BlockLike::NotBlock,
+        };
+
+        // test match_arms_commas
+        // fn foo() {
+        //     match () {
+        //         _ => (),
+        //         _ => {}
+        //         _ => ()
+        //     }
+        // }
+        if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) {
+            p.error("expected `,`");
+        }
     }
     m.complete(p, MATCH_ARM);
 }
@@ -579,7 +599,11 @@ fn match_guard(p: &mut Parser<'_>) -> CompletedMarker {
     assert!(p.at(T![if]));
     let m = p.start();
     p.bump(T![if]);
-    expr(p);
+    if p.at(T![=]) {
+        p.error("expected expression");
+    } else {
+        expr(p);
+    }
     m.complete(p, MATCH_GUARD)
 }
 


Test patch:
diff --git a/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast
new file mode 100644
index 0000000000..5b191945e4
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast
@@ -0,0 +1,113 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        MATCH_EXPR
+          MATCH_KW "match"
+          WHITESPACE " "
+          TUPLE_EXPR
+            L_PAREN "("
+            R_PAREN ")"
+          WHITESPACE " "
+          MATCH_ARM_LIST
+            L_CURLY "{"
+            WHITESPACE "\n        "
+            MATCH_ARM
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            ERROR
+              COMMA ","
+            WHITESPACE "\n        "
+            MATCH_ARM
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              COMMA ","
+            WHITESPACE "\n        "
+            MATCH_ARM
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            WHITESPACE "\n         "
+            MATCH_ARM
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            WHITESPACE "\n        "
+            MATCH_ARM
+              MATCH_GUARD
+                IF_KW "if"
+                WHITESPACE " "
+                LITERAL
+                  TRUE_KW "true"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            WHITESPACE "\n        "
+            MATCH_ARM
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            WHITESPACE "\n        "
+            MATCH_ARM
+              TUPLE_PAT
+                L_PAREN "("
+                R_PAREN ")"
+              WHITESPACE " "
+              MATCH_GUARD
+                IF_KW "if"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              TUPLE_EXPR
+                L_PAREN "("
+                R_PAREN ")"
+              COMMA ","
+            WHITESPACE "\n    "
+            R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 42: expected pattern
+error 58: expected expression
+error 85: expected pattern
+error 100: expected pattern
+error 145: expected expression
diff --git a/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs
new file mode 100644
index 0000000000..173103b2e3
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs
@@ -0,0 +1,11 @@
+fn foo() {
+    match () {
+        _ => (),,
+        _ => ,
+        _ => (),
+         => (),
+        if true => (),
+        _ => (),
+        () if => (),
+    }
+}