about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-21 00:34:10 +0000
committerbors <bors@rust-lang.org>2021-09-21 00:34:10 +0000
commite7958d35ca2c898a223efe402481e0ecb854310a (patch)
tree9bb8ad5fa75707ff579d9402e28532c3f12590ce
parent60e70cc909f84e84ed3bca3fe3459ddceefdd08c (diff)
parent21eff8f050b80e28309f72f659339ecfba2e0344 (diff)
downloadrust-e7958d35ca2c898a223efe402481e0ecb854310a.tar.gz
rust-e7958d35ca2c898a223efe402481e0ecb854310a.zip
Auto merge of #87830 - hkmatsumoto:suggest-brackets-for-array-esque-block-expr, r=estebank
Suggest replacing braces for brackets on array-esque invalid block expr

Newcomers may write `{1, 2, 3}` for making arrays, and the current error message is not informative enough to quickly convince them what is needed to fix the error.

This PR implements a diagnostic for this case, and its output looks like this:
```text
error: this code is interpreted as a block expression, not an array
 --> src/lib.rs:1:22
  |
1 |   const FOO: [u8; 3] = {
  |  ______________________^
2 | |     1, 2, 3
3 | | };
  | |_^
  |
  = note: to define an array, one would use square brackets instead of curly braces
help: try using [] instead of {}
  |
1 | const FOO: [u8; 3] = [
2 |     1, 2, 3
3 | ];
  |
```

Fix #87672
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs58
-rw-r--r--src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs17
-rw-r--r--src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr49
3 files changed, 120 insertions, 4 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index d8f9fc9179e..c62ea66b693 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1204,7 +1204,7 @@ impl<'a> Parser<'a> {
         } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
             self.parse_closure_expr(attrs)
         } else if self.check(&token::OpenDelim(token::Bracket)) {
-            self.parse_array_or_repeat_expr(attrs)
+            self.parse_array_or_repeat_expr(attrs, token::Bracket)
         } else if self.check_path() {
             self.parse_path_start_expr(attrs)
         } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
@@ -1322,11 +1322,15 @@ impl<'a> Parser<'a> {
         self.maybe_recover_from_bad_qpath(expr, true)
     }
 
-    fn parse_array_or_repeat_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+    fn parse_array_or_repeat_expr(
+        &mut self,
+        attrs: AttrVec,
+        close_delim: token::DelimToken,
+    ) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
-        self.bump(); // `[`
+        self.bump(); // `[` or other open delim
 
-        let close = &token::CloseDelim(token::Bracket);
+        let close = &token::CloseDelim(close_delim);
         let kind = if self.eat(close) {
             // Empty vector
             ExprKind::Array(Vec::new())
@@ -1752,6 +1756,46 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn is_array_like_block(&mut self) -> bool {
+        self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
+            && self.look_ahead(2, |t| t == &token::Comma)
+            && self.look_ahead(3, |t| t.can_begin_expr())
+    }
+
+    /// Emits a suggestion if it looks like the user meant an array but
+    /// accidentally used braces, causing the code to be interpreted as a block
+    /// expression.
+    fn maybe_suggest_brackets_instead_of_braces(
+        &mut self,
+        lo: Span,
+        attrs: AttrVec,
+    ) -> Option<P<Expr>> {
+        let mut snapshot = self.clone();
+        match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
+            Ok(arr) => {
+                let hi = snapshot.prev_token.span;
+                self.struct_span_err(
+                    arr.span,
+                    "this code is interpreted as a block expression, not an array",
+                )
+                .multipart_suggestion(
+                    "try using [] instead of {}",
+                    vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
+                    Applicability::MaybeIncorrect,
+                )
+                .note("to define an array, one would use square brackets instead of curly braces")
+                .emit();
+
+                *self = snapshot;
+                Some(self.mk_expr_err(arr.span))
+            }
+            Err(mut e) => {
+                e.cancel();
+                None
+            }
+        }
+    }
+
     /// Parses a block or unsafe block.
     pub(super) fn parse_block_expr(
         &mut self,
@@ -1760,6 +1804,12 @@ impl<'a> Parser<'a> {
         blk_mode: BlockCheckMode,
         mut attrs: AttrVec,
     ) -> PResult<'a, P<Expr>> {
+        if self.is_array_like_block() {
+            if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo, attrs.clone()) {
+                return Ok(arr);
+            }
+        }
+
         if let Some(label) = opt_label {
             self.sess.gated_spans.gate(sym::label_break_value, label.ident.span);
         }
diff --git a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs
new file mode 100644
index 00000000000..53dad85900a
--- /dev/null
+++ b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs
@@ -0,0 +1,17 @@
+fn main() {}
+
+const FOO: [u8; 3] = { //~ ERROR this code is interpreted as a block expression
+    1, 2, 3
+};
+
+const BAR: [&str; 3] = {"one", "two", "three"};
+//~^ ERROR this code is interpreted as a block expression
+
+fn foo() {
+    {1, 2, 3};
+    //~^ ERROR this code is interpreted as a block expression
+}
+
+fn bar() {
+    1, 2, 3 //~ ERROR expected one of
+}
diff --git a/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr
new file mode 100644
index 00000000000..9ab491f5c23
--- /dev/null
+++ b/src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr
@@ -0,0 +1,49 @@
+error: this code is interpreted as a block expression, not an array
+  --> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22
+   |
+LL |   const FOO: [u8; 3] = {
+   |  ______________________^
+LL | |     1, 2, 3
+LL | | };
+   | |_^
+   |
+   = note: to define an array, one would use square brackets instead of curly braces
+help: try using [] instead of {}
+   |
+LL ~ const FOO: [u8; 3] = [
+LL |     1, 2, 3
+LL ~ ];
+   |
+
+error: this code is interpreted as a block expression, not an array
+  --> $DIR/issue-87830-try-brackets-for-arrays.rs:7:24
+   |
+LL | const BAR: [&str; 3] = {"one", "two", "three"};
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: to define an array, one would use square brackets instead of curly braces
+help: try using [] instead of {}
+   |
+LL | const BAR: [&str; 3] = ["one", "two", "three"];
+   |                        ~                     ~
+
+error: this code is interpreted as a block expression, not an array
+  --> $DIR/issue-87830-try-brackets-for-arrays.rs:11:5
+   |
+LL |     {1, 2, 3};
+   |     ^^^^^^^^^
+   |
+   = note: to define an array, one would use square brackets instead of curly braces
+help: try using [] instead of {}
+   |
+LL |     [1, 2, 3];
+   |     ~       ~
+
+error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
+  --> $DIR/issue-87830-try-brackets-for-arrays.rs:16:6
+   |
+LL |     1, 2, 3
+   |      ^ expected one of `.`, `;`, `?`, `}`, or an operator
+
+error: aborting due to 4 previous errors
+