Rollup merge of #60188 - estebank:recover-block, r=varkor
Identify when a stmt could have been parsed as an expr There are some expressions that can be parsed as a statement without a trailing semicolon depending on the context, which can lead to confusing errors due to the same looking code being accepted in some places and not others. Identify these cases and suggest enclosing in parenthesis making the parse non-ambiguous without changing the accepted grammar. Fix #54186, cc #54482, fix #59975, fix #47287.
This commit is contained in:
commit
39edc68c69
17 changed files with 312 additions and 24 deletions
|
@ -186,6 +186,7 @@ enum PrevTokenKind {
|
|||
Interpolated,
|
||||
Eof,
|
||||
Ident,
|
||||
BitOr,
|
||||
Other,
|
||||
}
|
||||
|
||||
|
@ -1375,6 +1376,7 @@ impl<'a> Parser<'a> {
|
|||
token::DocComment(..) => PrevTokenKind::DocComment,
|
||||
token::Comma => PrevTokenKind::Comma,
|
||||
token::BinOp(token::Plus) => PrevTokenKind::Plus,
|
||||
token::BinOp(token::Or) => PrevTokenKind::BitOr,
|
||||
token::Interpolated(..) => PrevTokenKind::Interpolated,
|
||||
token::Eof => PrevTokenKind::Eof,
|
||||
token::Ident(..) => PrevTokenKind::Ident,
|
||||
|
@ -2806,6 +2808,12 @@ impl<'a> Parser<'a> {
|
|||
let msg = format!("expected expression, found {}",
|
||||
self.this_token_descr());
|
||||
let mut err = self.fatal(&msg);
|
||||
let sp = self.sess.source_map().start_point(self.span);
|
||||
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow()
|
||||
.get(&sp)
|
||||
{
|
||||
self.sess.expr_parentheses_needed(&mut err, *sp, None);
|
||||
}
|
||||
err.span_label(self.span, "expected expression");
|
||||
return Err(err);
|
||||
}
|
||||
|
@ -2845,7 +2853,7 @@ impl<'a> Parser<'a> {
|
|||
"struct literals are not allowed here",
|
||||
);
|
||||
err.multipart_suggestion(
|
||||
"surround the struct literal with parenthesis",
|
||||
"surround the struct literal with parentheses",
|
||||
vec![
|
||||
(lo.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
|
@ -3506,9 +3514,42 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
if self.expr_is_complete(&lhs) {
|
||||
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
|
||||
return Ok(lhs);
|
||||
match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) {
|
||||
(true, None) => {
|
||||
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
|
||||
return Ok(lhs);
|
||||
}
|
||||
(false, _) => {} // continue parsing the expression
|
||||
// An exhaustive check is done in the following block, but these are checked first
|
||||
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we
|
||||
// want to keep their span info to improve diagnostics in these cases in a later stage.
|
||||
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
|
||||
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
|
||||
(true, Some(AssocOp::Add)) => { // `{ 42 } + 42
|
||||
// These cases are ambiguous and can't be identified in the parser alone
|
||||
let sp = self.sess.source_map().start_point(self.span);
|
||||
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
|
||||
return Ok(lhs);
|
||||
}
|
||||
(true, Some(ref op)) if !op.can_continue_expr_unambiguously() => {
|
||||
return Ok(lhs);
|
||||
}
|
||||
(true, Some(_)) => {
|
||||
// We've found an expression that would be parsed as a statement, but the next
|
||||
// token implies this should be parsed as an expression.
|
||||
// For example: `if let Some(x) = x { x } else { 0 } / 2`
|
||||
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &format!(
|
||||
"expected expression, found `{}`",
|
||||
pprust::token_to_string(&self.token),
|
||||
));
|
||||
err.span_label(self.span, "expected expression");
|
||||
self.sess.expr_parentheses_needed(
|
||||
&mut err,
|
||||
lhs.span,
|
||||
Some(pprust::expr_to_string(&lhs),
|
||||
));
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
self.expected_tokens.push(TokenType::Operator);
|
||||
while let Some(op) = AssocOp::from_token(&self.token) {
|
||||
|
@ -4819,6 +4860,10 @@ impl<'a> Parser<'a> {
|
|||
);
|
||||
let mut err = self.fatal(&msg);
|
||||
err.span_label(self.span, format!("expected {}", expected));
|
||||
let sp = self.sess.source_map().start_point(self.span);
|
||||
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
|
||||
self.sess.expr_parentheses_needed(&mut err, *sp, None);
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue