Rollup merge of #82714 - estebank:missing-braces, r=oli-obk
Detect match arm body without braces Fix #82524.
This commit is contained in:
commit
34b2caa79f
@ -1973,6 +1973,102 @@ fn parse_match_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> {
|
||||
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 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
|
||||
|
||||
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)) {
|
||||
|
@ -34,7 +34,7 @@ pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<
|
||||
|
||||
/// 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,
|
||||
|
87
src/test/ui/parser/match-arm-without-braces.rs
Normal file
87
src/test/ui/parser/match-arm-without-braces.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
135
src/test/ui/parser/match-arm-without-braces.stderr
Normal file
135
src/test/ui/parser/match-arm-without-braces.stderr
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user