From e5f83bcd04e85b8ae5f04d2d95dd9af774d422e2 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 12 Aug 2020 15:39:15 -0700 Subject: 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. --- compiler/rustc_parse/src/parser/expr.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'compiler/rustc_parse/src/parser/expr.rs') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 69d13b5cf53..0eac04df3c9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2015,9 +2015,12 @@ impl<'a> Parser<'a> { ) -> Option>> { 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); @@ -2035,12 +2038,13 @@ impl<'a> Parser<'a> { .emit(); } + /// Precondition: already parsed the '{'. pub(super) fn parse_struct_expr( &mut self, pth: ast::Path, mut attrs: AttrVec, + recover: bool, ) -> PResult<'a, P> { - self.bump(); let mut fields = Vec::new(); let mut base = None; let mut recover_async = false; @@ -2059,10 +2063,11 @@ impl<'a> Parser<'a> { 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; @@ -2114,6 +2119,9 @@ impl<'a> Parser<'a> { ); } } + if !recover { + return Err(e); + } e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); self.eat(&token::Comma); -- cgit 1.4.1-3-g733a5