Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 3 additions & 39 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,9 @@ impl<'a> Parser<'a> {
if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {
return Ok(expr);
}
if let Some(arr) = this.recover_from_c_array(lo) {
return Ok(arr);
}
this.parse_expr_block(None, lo, BlockCheckMode::Default)
} else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {
this.parse_expr_closure().map_err(|mut err| {
Expand Down Expand Up @@ -2227,39 +2230,6 @@ impl<'a> Parser<'a> {
}
}

fn is_array_like_block(&mut self) -> bool {
self.token.kind == TokenKind::OpenBrace
&& 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) -> Option<Box<Expr>> {
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(crate::diagnostics::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: crate::diagnostics::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
});

self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span, guar))
}
Err(e) => {
e.cancel();
None
}
}
}

fn suggest_missing_semicolon_before_array(
&self,
prev_span: Span,
Expand Down Expand Up @@ -2309,12 +2279,6 @@ impl<'a> Parser<'a> {
lo: Span,
blk_mode: BlockCheckMode,
) -> PResult<'a, Box<Expr>> {
if self.may_recover() && self.is_array_like_block() {
if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) {
return Ok(arr);
}
}

if self.token.is_metavar_block() {
self.dcx().emit_err(crate::diagnostics::InvalidBlockMacroSegment {
span: self.token.span,
Expand Down
34 changes: 33 additions & 1 deletion compiler/rustc_parse/src/parser/expr/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use rustc_ast::{BinOpKind, Expr, ExprKind, token};
use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::{Span, Spanned, respan, sym};

use crate::diagnostics;
use crate::parser::Parser;
use crate::{diagnostics, exp};

impl<'a> Parser<'a> {
/// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP.
Expand Down Expand Up @@ -216,6 +216,38 @@ impl<'a> Parser<'a> {
}
err
}

/// Recover from array expressions as found in C like `{0, 1, 2, 3}`.
pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option<Box<Expr>> {
if !self.may_recover()
|| self.token.kind != token::OpenBrace
|| self.look_ahead(1, |t| !matches!(t.kind, token::Literal(_)))
|| self.look_ahead(2, |t| t != &token::Comma)
|| self.look_ahead(3, |t| !t.can_begin_expr())
{
return None;
}

let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: diagnostics::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
});

self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span, guar))
}
Err(e) => {
e.cancel();
None
}
}
}
}

#[derive(Copy, Clone)]
Expand Down
32 changes: 28 additions & 4 deletions tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
// Test that we can recover from very basic C arrays in the parser & provide a good diagnostic.

fn main() {}

const FOO: [u8; 3] = {
const INTS: [u8; 3] = {
//~^ ERROR this is a block expression, not an array
1, 2, 3
};

const BAR: [&str; 3] = {"one", "two", "three"};
const STRS: [&str; 3] = {"one", "two", "three"};
//~^ ERROR this is a block expression, not an array

fn foo() {
fn expr_stmt() {
{1, 2, 3};
//~^ ERROR this is a block expression, not an array
}

fn bar() {
// Don't trigger here.
fn unsafe_block() {
unsafe { 1, 2, 3 } //~ ERROR expected one of
}

// Don't trigger here.
fn labeled_block() {
'label: { 1, 2, 3 } //~ ERROR expected one of
}

// Don't trigger here, this is not a block expression, only a block.
fn fn_body_block() {
1, 2, 3 //~ ERROR expected one of
}

// Don't trigger here, this is not a block expression, only a block.
fn closure_body_block() {
|| -> i32 { 1, 2, 3 }; //~ ERROR expected one of
}

// Don't trigger here.
fn const_arg() {
struct Casket<const N: usize>;
Casket::<{ 1, 2, 3 }>; //~ ERROR expected one of
}
48 changes: 36 additions & 12 deletions tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
error: this is a block expression, not an array
--> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22
--> $DIR/issue-87830-try-brackets-for-arrays.rs:5:23
|
LL | const FOO: [u8; 3] = {
| ______________________^
LL | const INTS: [u8; 3] = {
| _______________________^
LL | |
LL | | 1, 2, 3
LL | | };
| |_^
|
help: to make an array, use square brackets instead of curly braces
|
LL ~ const FOO: [u8; 3] = [
LL ~ const INTS: [u8; 3] = [
LL |
LL | 1, 2, 3
LL ~ ];
|

error: this is a block expression, not an array
--> $DIR/issue-87830-try-brackets-for-arrays.rs:8:24
--> $DIR/issue-87830-try-brackets-for-arrays.rs:10:25
|
LL | const BAR: [&str; 3] = {"one", "two", "three"};
| ^^^^^^^^^^^^^^^^^^^^^^^
LL | const STRS: [&str; 3] = {"one", "two", "three"};
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: to make an array, use square brackets instead of curly braces
|
LL - const BAR: [&str; 3] = {"one", "two", "three"};
LL + const BAR: [&str; 3] = ["one", "two", "three"];
LL - const STRS: [&str; 3] = {"one", "two", "three"};
LL + const STRS: [&str; 3] = ["one", "two", "three"];
|

error: this is a block expression, not an array
--> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5
--> $DIR/issue-87830-try-brackets-for-arrays.rs:14:5
|
LL | {1, 2, 3};
| ^^^^^^^^^
Expand All @@ -41,10 +41,34 @@ LL + [1, 2, 3];
|

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
--> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6
--> $DIR/issue-87830-try-brackets-for-arrays.rs:20:15
|
LL | unsafe { 1, 2, 3 }
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
--> $DIR/issue-87830-try-brackets-for-arrays.rs:25:16
|
LL | 'label: { 1, 2, 3 }
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
--> $DIR/issue-87830-try-brackets-for-arrays.rs:30:6
|
LL | 1, 2, 3
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: aborting due to 4 previous errors
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
--> $DIR/issue-87830-try-brackets-for-arrays.rs:35:18
|
LL | || -> i32 { 1, 2, 3 };
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
--> $DIR/issue-87830-try-brackets-for-arrays.rs:41:17
|
LL | Casket::<{ 1, 2, 3 }>;
| ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: aborting due to 8 previous errors

Loading