use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{struct_span_err, Applicability, PResult};
use rustc_feature::Features;
-use rustc_parse::parser::Parser;
+use rustc_parse::parser::{AttemptLocalParseRecovery, Parser};
use rustc_parse::validate_attr;
use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
use rustc_session::lint::BuiltinLintDiagnostics;
let mut stmts = SmallVec::new();
// Won't make progress on a `}`.
while this.token != token::Eof && this.token != token::CloseDelim(token::Brace) {
- if let Some(stmt) = this.parse_full_stmt()? {
+ if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
stmts.push(stmt);
}
}
use rustc_ast::token::{self, Lit, LitKind, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
- self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr,
- ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
+ self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr,
+ ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty,
+ TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
No,
}
+#[derive(Clone, Copy)]
+pub enum AttemptLocalParseRecovery {
+ Yes,
+ No,
+}
+
+impl AttemptLocalParseRecovery {
+ pub fn yes(&self) -> bool {
+ match self {
+ AttemptLocalParseRecovery::Yes => true,
+ AttemptLocalParseRecovery::No => false,
+ }
+ }
+
+ pub fn no(&self) -> bool {
+ match self {
+ AttemptLocalParseRecovery::Yes => false,
+ AttemptLocalParseRecovery::No => true,
+ }
+ }
+}
+
impl<'a> Parser<'a> {
pub(super) fn span_fatal_err<S: Into<MultiSpan>>(
&self,
}
}
+ pub fn maybe_suggest_struct_literal(
+ &mut self,
+ lo: Span,
+ s: BlockCheckMode,
+ ) -> Option<PResult<'a, P<Block>>> {
+ if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
+ // We might be having a struct literal where people forgot to include the path:
+ // fn foo() -> Foo {
+ // field: value,
+ // }
+ let mut snapshot = self.clone();
+ let path =
+ Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None };
+ let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false);
+ let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
+ return Some(match (struct_expr, block_tail) {
+ (Ok(expr), Err(mut err)) => {
+ // We have encountered the following:
+ // fn foo() -> Foo {
+ // field: value,
+ // }
+ // Suggest:
+ // fn foo() -> Foo { Path {
+ // field: value,
+ // } }
+ err.delay_as_bug();
+ self.struct_span_err(expr.span, "struct literal body without path")
+ .multipart_suggestion(
+ "you might have forgotten to add the struct literal inside the block",
+ vec![
+ (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
+ (expr.span.shrink_to_hi(), " }".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ *self = snapshot;
+ Ok(self.mk_block(
+ vec![self.mk_stmt_err(expr.span)],
+ s,
+ lo.to(self.prev_token.span),
+ ))
+ }
+ (Err(mut err), Ok(tail)) => {
+ // We have a block tail that contains a somehow valid type ascription expr.
+ err.cancel();
+ Ok(tail)
+ }
+ (Err(mut snapshot_err), Err(err)) => {
+ // We don't know what went wrong, emit the normal error.
+ snapshot_err.cancel();
+ self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+ Err(err)
+ }
+ (Ok(_), Ok(tail)) => Ok(tail),
+ });
+ }
+ None
+ }
+
pub fn maybe_annotate_with_ascription(
&mut self,
err: &mut DiagnosticBuilder<'_>,
) -> Option<PResult<'a, P<Expr>>> {
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
if struct_allowed || self.is_certainly_not_a_block() {
- // This is a struct literal, but we don't can't accept them here.
- let expr = self.parse_struct_expr(path.clone(), attrs.clone());
+ if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) {
+ return Some(Err(err));
+ }
+ let expr = self.parse_struct_expr(path.clone(), attrs.clone(), true);
if let (Ok(expr), false) = (&expr, struct_allowed) {
+ // This is a struct literal, but we don't can't accept them here.
self.error_struct_lit_not_allowed_here(path.span, expr.span);
}
return Some(expr);
.emit();
}
+ /// Precondition: already parsed the '{'.
pub(super) fn parse_struct_expr(
&mut self,
pth: ast::Path,
mut attrs: AttrVec,
+ recover: bool,
) -> PResult<'a, P<Expr>> {
- self.bump();
let mut fields = Vec::new();
let mut base = None;
let mut recover_async = false;
let exp_span = self.prev_token.span;
match self.parse_expr() {
Ok(e) => base = Some(e),
- Err(mut e) => {
+ Err(mut e) if recover => {
e.emit();
self.recover_stmt();
}
+ Err(e) => return Err(e),
}
self.recover_struct_comma_after_dotdot(exp_span);
break;
);
}
}
+ if !recover {
+ return Err(e);
+ }
e.emit();
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
self.eat(&token::Comma);
mod ty;
use crate::lexer::UnmatchedBrace;
+pub use diagnostics::AttemptLocalParseRecovery;
use diagnostics::Error;
pub use path::PathStyle;
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;
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())
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);
}
/// 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));
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();
}
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)))
}
--- /dev/null
+struct Foo {
+ val: (),
+}
+
+fn foo() -> Foo { //~ ERROR struct literal body without path
+ val: (),
+}
+
+fn main() {
+ let x = foo();
+ x.val == 42; //~ ERROR mismatched types
+ let x = { //~ ERROR struct literal body without path
+ val: (),
+ };
+}
--- /dev/null
+error: struct literal body without path
+ --> $DIR/bare-struct-body.rs:5:17
+ |
+LL | fn foo() -> Foo {
+ | _________________^
+LL | | val: (),
+LL | | }
+ | |_^
+ |
+help: you might have forgotten to add the struct literal inside the block
+ |
+LL | fn foo() -> Foo { SomeStruct {
+LL | val: (),
+LL | } }
+ |
+
+error: struct literal body without path
+ --> $DIR/bare-struct-body.rs:12:13
+ |
+LL | let x = {
+ | _____________^
+LL | | val: (),
+LL | | };
+ | |_____^
+ |
+help: you might have forgotten to add the struct literal inside the block
+ |
+LL | let x = { SomeStruct {
+LL | val: (),
+LL | } };
+ |
+
+error[E0308]: mismatched types
+ --> $DIR/bare-struct-body.rs:11:14
+ |
+LL | x.val == 42;
+ | ^^ expected `()`, found integer
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.