From 21eff8f050b80e28309f72f659339ecfba2e0344 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 7 Aug 2021 04:34:29 +0900 Subject: [PATCH] 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 --- compiler/rustc_parse/src/parser/expr.rs | 58 +++++++++++++++++-- .../issue-87830-try-brackets-for-arrays.rs | 17 ++++++ ...issue-87830-try-brackets-for-arrays.stderr | 49 ++++++++++++++++ 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs create mode 100644 src/test/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr 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 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P> { } 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 @@ fn parse_tuple_parens_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { self.maybe_recover_from_bad_qpath(expr, true) } - fn parse_array_or_repeat_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + fn parse_array_or_repeat_expr( + &mut self, + attrs: AttrVec, + close_delim: token::DelimToken, + ) -> PResult<'a, P> { 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 @@ pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { } } + 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> { + 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 @@ pub(super) fn parse_block_expr( blk_mode: BlockCheckMode, mut attrs: AttrVec, ) -> PResult<'a, P> { + 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 +