Detect blocks that could be struct expr bodies

This approach lives exclusively in the parser, so struct expr bodies
that are syntactically correct on their own but are otherwise incorrect
will still emit confusing errors, like in the following case:

```rust
fn foo() -> Foo {
    bar: Vec::new()
}
```

```
error[E0425]: cannot find value `bar` in this scope
 --> src/file.rs:5:5
  |
5 |     bar: Vec::new()
  |     ^^^ expecting a type here because of type ascription

error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
 --> src/file.rs:5:15
  |
5 |     bar: Vec::new()
  |               ^^^^^ only `Fn` traits may use parentheses

error[E0107]: wrong number of type arguments: expected 1, found 0
 --> src/file.rs:5:10
  |
5 |     bar: Vec::new()
  |          ^^^^^^^^^^ expected 1 type argument
  ```

If that field had a trailing comma, that would be a parse error and it
would trigger the new, more targetted, error:

```
error: struct literal body without path
 --> file.rs:4:17
  |
4 |   fn foo() -> Foo {
  |  _________________^
5 | |     bar: Vec::new(),
6 | | }
  | |_^
  |
help: you might have forgotten to add the struct literal inside the block
  |
4 | fn foo() -> Foo { Path {
5 |     bar: Vec::new(),
6 | } }
  |
```

Partially address last part of #34255.
This commit is contained in:
Esteban Küber 2020-08-12 15:39:15 -07:00
parent deec530523
commit e5f83bcd04
7 changed files with 183 additions and 17 deletions

View file

@ -1,5 +1,5 @@
use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
use super::diagnostics::Error;
use super::diagnostics::{AttemptLocalParseRecovery, Error};
use super::expr::LhsExpr;
use super::pat::GateOr;
use super::path::PathStyle;
@ -79,8 +79,8 @@ impl<'a> Parser<'a> {
return self.parse_stmt_mac(lo, attrs.into(), path);
}
let expr = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_struct_expr(path, AttrVec::new())?
let expr = if self.eat(&token::OpenDelim(token::Brace)) {
self.parse_struct_expr(path, AttrVec::new(), true)?
} else {
let hi = self.prev_token.span;
self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new())
@ -321,25 +321,37 @@ impl<'a> Parser<'a> {
return self.error_block_no_opening_brace();
}
Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, blk_mode)?))
let attrs = self.parse_inner_attributes()?;
let tail = if let Some(tail) = self.maybe_suggest_struct_literal(lo, blk_mode) {
tail?
} else {
self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?
};
Ok((attrs, tail))
}
/// Parses the rest of a block expression or function body.
/// Precondition: already parsed the '{'.
fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P<Block>> {
crate fn parse_block_tail(
&mut self,
lo: Span,
s: BlockCheckMode,
recover: AttemptLocalParseRecovery,
) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
while !self.eat(&token::CloseDelim(token::Brace)) {
if self.token == token::Eof {
break;
}
let stmt = match self.parse_full_stmt() {
Err(mut err) => {
let stmt = match self.parse_full_stmt(recover) {
Err(mut err) if recover.yes() => {
self.maybe_annotate_with_ascription(&mut err, false);
err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(self.mk_stmt_err(self.token.span))
}
Ok(stmt) => stmt,
Err(err) => return Err(err),
};
if let Some(stmt) = stmt {
stmts.push(stmt);
@ -352,7 +364,10 @@ impl<'a> Parser<'a> {
}
/// Parses a statement, including the trailing semicolon.
pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
pub fn parse_full_stmt(
&mut self,
recover: AttemptLocalParseRecovery,
) -> PResult<'a, Option<Stmt>> {
// Skip looking for a trailing semicolon when we have an interpolated statement.
maybe_whole!(self, NtStmt, |x| Some(x));
@ -391,6 +406,9 @@ impl<'a> Parser<'a> {
if let Err(mut e) =
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
{
if recover.no() {
return Err(e);
}
e.emit();
self.recover_stmt();
}
@ -432,7 +450,7 @@ impl<'a> Parser<'a> {
Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None }
}
fn mk_stmt_err(&self, span: Span) -> Stmt {
pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt {
self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span)))
}