Skip to content

Commit

Permalink
Suggest replacing braces for brackets on array-esque invalid block expr
Browse files Browse the repository at this point in the history
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
  • Loading branch information
hkmatsumoto committed Sep 19, 2021
1 parent 8e398f5 commit 21eff8f
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 4 deletions.
58 changes: 54 additions & 4 deletions compiler/rustc_parse/src/parser/expr.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down
17 changes: 17 additions & 0 deletions 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
}
@@ -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

0 comments on commit 21eff8f

Please sign in to comment.