about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2021-03-05 21:44:39 +0100
committerGitHub <noreply@github.com>2021-03-05 21:44:39 +0100
commit34b2caa79f4451ff7eede9a578295e1a48db4bf2 (patch)
tree40012ecff7e8af0873cb7b07c3c7ac437e38e6fc
parent8867f7f6507ad0a6682c969235fb5f0b572e646b (diff)
parentae494d147af627537097f10f21cf961e4c1c6f38 (diff)
downloadrust-34b2caa79f4451ff7eede9a578295e1a48db4bf2.tar.gz
rust-34b2caa79f4451ff7eede9a578295e1a48db4bf2.zip
Rollup merge of #82714 - estebank:missing-braces, r=oli-obk
Detect match arm body without braces

Fix #82524.
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs111
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs2
-rw-r--r--src/test/ui/parser/match-arm-without-braces.rs87
-rw-r--r--src/test/ui/parser/match-arm-without-braces.stderr135
4 files changed, 334 insertions, 1 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 28bfaea4555..608b0248274 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1973,6 +1973,102 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs))
     }
 
+    /// Attempt to recover from match arm body with statements and no surrounding braces.
+    fn parse_arm_body_missing_braces(
+        &mut self,
+        first_expr: &P<Expr>,
+        arrow_span: Span,
+    ) -> Option<P<Expr>> {
+        if self.token.kind != token::Semi {
+            return None;
+        }
+        let start_snapshot = self.clone();
+        let semi_sp = self.token.span;
+        self.bump(); // `;`
+        let mut stmts =
+            vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))];
+        let err = |this: &mut Parser<'_>, stmts: Vec<ast::Stmt>| {
+            let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
+            let mut err = this.struct_span_err(span, "`match` arm body without braces");
+            let (these, s, are) =
+                if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") };
+            err.span_label(
+                span,
+                &format!(
+                    "{these} statement{s} {are} not surrounded by a body",
+                    these = these,
+                    s = s,
+                    are = are
+                ),
+            );
+            err.span_label(arrow_span, "while parsing the `match` arm starting here");
+            if stmts.len() > 1 {
+                err.multipart_suggestion(
+                    &format!("surround the statement{} with a body", s),
+                    vec![
+                        (span.shrink_to_lo(), "{ ".to_string()),
+                        (span.shrink_to_hi(), " }".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                err.span_suggestion(
+                    semi_sp,
+                    "use a comma to end a `match` arm expression",
+                    ",".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            }
+            err.emit();
+            this.mk_expr_err(span)
+        };
+        // We might have either a `,` -> `;` typo, or a block without braces. We need
+        // a more subtle parsing strategy.
+        loop {
+            if self.token.kind == token::CloseDelim(token::Brace) {
+                // We have reached the closing brace of the `match` expression.
+                return Some(err(self, stmts));
+            }
+            if self.token.kind == token::Comma {
+                *self = start_snapshot;
+                return None;
+            }
+            let pre_pat_snapshot = self.clone();
+            match self.parse_pat_no_top_alt(None) {
+                Ok(_pat) => {
+                    if self.token.kind == token::FatArrow {
+                        // Reached arm end.
+                        *self = pre_pat_snapshot;
+                        return Some(err(self, stmts));
+                    }
+                }
+                Err(mut err) => {
+                    err.cancel();
+                }
+            }
+
+            *self = pre_pat_snapshot;
+            match self.parse_stmt_without_recovery(true, ForceCollect::No) {
+                // Consume statements for as long as possible.
+                Ok(Some(stmt)) => {
+                    stmts.push(stmt);
+                }
+                Ok(None) => {
+                    *self = start_snapshot;
+                    break;
+                }
+                // We couldn't parse either yet another statement missing it's
+                // enclosing block nor the next arm's pattern or closing brace.
+                Err(mut stmt_err) => {
+                    stmt_err.cancel();
+                    *self = start_snapshot;
+                    break;
+                }
+            }
+        }
+        None
+    }
+
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
         self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
@@ -2007,6 +2103,21 @@ impl<'a> Parser<'a> {
 
             if require_comma {
                 let sm = this.sess.source_map();
+                if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) {
+                    let span = body.span;
+                    return Ok((
+                        ast::Arm {
+                            attrs,
+                            pat,
+                            guard,
+                            body,
+                            span,
+                            id: DUMMY_NODE_ID,
+                            is_placeholder: false,
+                        },
+                        TrailingToken::None,
+                    ));
+                }
                 this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err(
                     |mut err| {
                         match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) {
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 07746f2390d..a0f9616f72a 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -34,7 +34,7 @@ impl<'a> Parser<'a> {
 
     /// If `force_capture` is true, forces collection of tokens regardless of whether
     /// or not we have attributes
-    fn parse_stmt_without_recovery(
+    crate fn parse_stmt_without_recovery(
         &mut self,
         capture_semi: bool,
         force_collect: ForceCollect,
diff --git a/src/test/ui/parser/match-arm-without-braces.rs b/src/test/ui/parser/match-arm-without-braces.rs
new file mode 100644
index 00000000000..55a88742769
--- /dev/null
+++ b/src/test/ui/parser/match-arm-without-braces.rs
@@ -0,0 +1,87 @@
+struct S;
+
+impl S {
+    fn get<K, V: Default>(_: K) -> Option<V> {
+        Default::default()
+    }
+}
+
+enum Val {
+    Foo,
+    Bar,
+}
+
+impl Default for Val {
+    fn default() -> Self {
+        Val::Foo
+    }
+}
+
+fn main() {
+    match S::get(1) {
+        Some(Val::Foo) => {}
+        _ => {}
+    }
+    match S::get(2) {
+        Some(Val::Foo) => 3; //~ ERROR `match` arm body without braces
+        _ => 4,
+    }
+    match S::get(5) {
+        Some(Val::Foo) =>
+          7; //~ ERROR `match` arm body without braces
+          8;
+        _ => 9,
+    }
+    match S::get(10) {
+        Some(Val::Foo) =>
+          11; //~ ERROR `match` arm body without braces
+          12;
+        _ => (),
+    }
+    match S::get(13) {
+        None => {}
+        Some(Val::Foo) =>
+          14; //~ ERROR `match` arm body without braces
+          15;
+    }
+    match S::get(16) {
+        Some(Val::Foo) => 17
+        _ => 18, //~ ERROR expected one of
+    }
+    match S::get(19) {
+        Some(Val::Foo) =>
+          20; //~ ERROR `match` arm body without braces
+          21
+        _ => 22,
+    }
+    match S::get(23) {
+        Some(Val::Foo) =>
+          24; //~ ERROR `match` arm body without braces
+          25
+        _ => (),
+    }
+    match S::get(26) {
+        None => {}
+        Some(Val::Foo) =>
+          27; //~ ERROR `match` arm body without braces
+          28
+    }
+    match S::get(29) {
+        Some(Val::Foo) =>
+          30; //~ ERROR expected one of
+          31,
+        _ => 32,
+    }
+    match S::get(33) {
+        Some(Val::Foo) =>
+          34; //~ ERROR expected one of
+          35,
+        _ => (),
+    }
+    match S::get(36) {
+        None => {}
+        Some(Val::Foo) =>
+          37; //~ ERROR expected one of
+          38,
+    }
+}
diff --git a/src/test/ui/parser/match-arm-without-braces.stderr b/src/test/ui/parser/match-arm-without-braces.stderr
new file mode 100644
index 00000000000..03ae351bf79
--- /dev/null
+++ b/src/test/ui/parser/match-arm-without-braces.stderr
@@ -0,0 +1,135 @@
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:26:27
+   |
+LL |         Some(Val::Foo) => 3;
+   |                        -- ^- help: use a comma to end a `match` arm expression: `,`
+   |                        |  |
+   |                        |  this statement is not surrounded by a body
+   |                        while parsing the `match` arm starting here
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:31:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           7;
+LL | |           8;
+   | |____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 7;
+LL |           8; }
+   |
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:37:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           11;
+LL | |           12;
+   | |_____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 11;
+LL |           12; }
+   |
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:44:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           14;
+LL | |           15;
+   | |_____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 14;
+LL |           15; }
+   |
+
+error: expected one of `,`, `.`, `?`, `}`, or an operator, found reserved identifier `_`
+  --> $DIR/match-arm-without-braces.rs:49:9
+   |
+LL |         Some(Val::Foo) => 17
+   |                        --   - expected one of `,`, `.`, `?`, `}`, or an operator
+   |                        |
+   |                        while parsing the `match` arm starting here
+LL |         _ => 18,
+   |         ^ unexpected token
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:53:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           20;
+LL | |           21
+   | |____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 20;
+LL |           21 }
+   |
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:59:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           24;
+LL | |           25
+   | |____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 24;
+LL |           25 }
+   |
+
+error: `match` arm body without braces
+  --> $DIR/match-arm-without-braces.rs:66:11
+   |
+LL |           Some(Val::Foo) =>
+   |                          -- while parsing the `match` arm starting here
+LL | /           27;
+LL | |           28
+   | |____________^ these statements are not surrounded by a body
+   |
+help: surround the statements with a body
+   |
+LL |           { 27;
+LL |           28 }
+   |
+
+error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;`
+  --> $DIR/match-arm-without-braces.rs:71:13
+   |
+LL |         Some(Val::Foo) =>
+   |                        -- while parsing the `match` arm starting here
+LL |           30;
+   |             ^ expected one of `,`, `.`, `?`, `}`, or an operator
+
+error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;`
+  --> $DIR/match-arm-without-braces.rs:77:13
+   |
+LL |         Some(Val::Foo) =>
+   |                        -- while parsing the `match` arm starting here
+LL |           34;
+   |             ^ expected one of `,`, `.`, `?`, `}`, or an operator
+
+error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;`
+  --> $DIR/match-arm-without-braces.rs:84:13
+   |
+LL |         Some(Val::Foo) =>
+   |                        -- while parsing the `match` arm starting here
+LL |           37;
+   |             ^ expected one of `,`, `.`, `?`, `}`, or an operator
+
+error: aborting due to 11 previous errors
+