diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 199 |
1 files changed, 140 insertions, 59 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f6a6ed379..bf93a89f0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -83,7 +83,7 @@ macro_rules! maybe_whole_expr { pub(super) enum LhsExpr { NotYetParsed, AttributesParsed(AttrWrapper), - AlreadyParsed(P<Expr>), + AlreadyParsed { expr: P<Expr>, starts_statement: bool }, } impl From<Option<AttrWrapper>> for LhsExpr { @@ -97,11 +97,11 @@ impl From<Option<AttrWrapper>> for LhsExpr { } impl From<P<Expr>> for LhsExpr { - /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed(expr)`. + /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`. /// /// This conversion does not allocate. fn from(expr: P<Expr>) -> Self { - LhsExpr::AlreadyParsed(expr) + LhsExpr::AlreadyParsed { expr, starts_statement: false } } } @@ -173,14 +173,16 @@ impl<'a> Parser<'a> { min_prec: usize, lhs: LhsExpr, ) -> PResult<'a, P<Expr>> { - let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { + let mut starts_stmt = false; + let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs { + starts_stmt = starts_statement; expr } else { let attrs = match lhs { LhsExpr::AttributesParsed(attrs) => Some(attrs), _ => None, }; - if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind) { + if self.token.is_range_separator() { return self.parse_prefix_range_expr(attrs); } else { self.parse_prefix_expr(attrs)? @@ -292,7 +294,7 @@ impl<'a> Parser<'a> { let op_span = self.prev_token.span.to(self.token.span); // Eat the second `+` self.bump(); - lhs = self.recover_from_postfix_increment(lhs, op_span)?; + lhs = self.recover_from_postfix_increment(lhs, op_span, starts_stmt)?; continue; } @@ -512,7 +514,7 @@ impl<'a> Parser<'a> { } debug_assert!( - [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind), + self.token.is_range_separator(), "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", self.token ); @@ -560,17 +562,23 @@ impl<'a> Parser<'a> { // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() match this.token.uninterpolate().kind { - token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), // `!expr` - token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `~expr` + // `!expr` + token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), + // `~expr` + token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), + // `-expr` token::BinOp(token::Minus) => { make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg)) - } // `-expr` + } + // `*expr` token::BinOp(token::Star) => { make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref)) - } // `*expr` + } + // `&expr` and `&&expr` token::BinOp(token::And) | token::AndAnd => { make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) } + // `+lit` token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { let mut err = LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None }; @@ -585,19 +593,20 @@ impl<'a> Parser<'a> { this.bump(); this.parse_prefix_expr(None) - } // `+expr` + } // Recover from `++x`: token::BinOp(token::Plus) if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) => { - let prev_is_semi = this.prev_token == token::Semi; + let starts_stmt = this.prev_token == token::Semi + || this.prev_token == token::CloseDelim(Delimiter::Brace); let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span)); // Eat both `+`s. this.bump(); this.bump(); let operand_expr = this.parse_dot_or_call_expr(Default::default())?; - this.recover_from_prefix_increment(operand_expr, pre_span, prev_is_semi) + this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) } token::Ident(..) if this.token.is_keyword(kw::Box) => { make_it!(this, attrs, |this, _| this.parse_box_expr(lo)) @@ -621,7 +630,7 @@ impl<'a> Parser<'a> { Ok((span, self.mk_unary(op, expr))) } - // Recover on `!` suggesting for bitwise negation instead. + /// Recover on `~expr` in favor of `!expr`. fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { self.sess.emit_err(TildeAsUnaryOperator(lo)); @@ -648,7 +657,6 @@ impl<'a> Parser<'a> { /// Recover on `not expr` in favor of `!expr`. fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - // Emit the error... let negated_token = self.look_ahead(1, |t| t.clone()); let sub_diag = if negated_token.is_numeric_lit() { @@ -669,7 +677,6 @@ impl<'a> Parser<'a> { ), }); - // ...and recover! self.parse_unary_expr(lo, UnOp::Not) } @@ -896,7 +903,11 @@ impl<'a> Parser<'a> { let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); - let expr = self.parse_prefix_expr(None); + let expr = if self.token.is_range_separator() { + self.parse_prefix_range_expr(None) + } else { + self.parse_prefix_expr(None) + }; let (hi, expr) = self.interpolated_or_expr_span(expr)?; let span = lo.to(hi); if let Some(lt) = lifetime { @@ -1318,7 +1329,10 @@ impl<'a> Parser<'a> { self.parse_array_or_repeat_expr(Delimiter::Bracket) } else if self.check_path() { self.parse_path_start_expr() - } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { + } else if self.check_keyword(kw::Move) + || self.check_keyword(kw::Static) + || self.check_const_closure() + { self.parse_closure_expr() } else if self.eat_keyword(kw::If) { self.parse_if_expr() @@ -1339,9 +1353,6 @@ impl<'a> Parser<'a> { err.span_label(sp, "while parsing this `loop` expression"); err }) - } else if self.eat_keyword(kw::Continue) { - let kind = ExprKind::Continue(self.eat_label()); - Ok(self.mk_expr(lo.to(self.prev_token.span), kind)) } else if self.eat_keyword(kw::Match) { let match_sp = self.prev_token.span; self.parse_match_expr().map_err(|mut err| { @@ -1365,6 +1376,8 @@ impl<'a> Parser<'a> { self.parse_try_block(lo) } else if self.eat_keyword(kw::Return) { self.parse_return_expr() + } else if self.eat_keyword(kw::Continue) { + self.parse_continue_expr(lo) } else if self.eat_keyword(kw::Break) { self.parse_break_expr() } else if self.eat_keyword(kw::Yield) { @@ -1461,9 +1474,8 @@ impl<'a> Parser<'a> { } else if self.eat(&token::Comma) { // Vector with two or more elements. let sep = SeqSep::trailing_allowed(token::Comma); - let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; - let mut exprs = vec![first_expr]; - exprs.extend(remaining_exprs); + let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; + exprs.insert(0, first_expr); ExprKind::Array(exprs) } else { // Vector with one element @@ -1496,12 +1508,13 @@ impl<'a> Parser<'a> { prior_type_ascription: self.last_type_ascription, }); (lo.to(self.prev_token.span), ExprKind::MacCall(mac)) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) && - let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { - if qself.is_some() { - self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); - } - return expr; + } else if self.check(&token::OpenDelim(Delimiter::Brace)) + && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) + { + if qself.is_some() { + self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); + } + return expr; } else { (path.span, ExprKind::Path(qself, path)) }; @@ -1534,15 +1547,16 @@ impl<'a> Parser<'a> { && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) || self.token.is_op()) { - let lit = self.recover_unclosed_char(label_.ident, |self_| { - self_.sess.create_err(UnexpectedTokenAfterLabel { - span: self_.token.span, - remove_label: None, - enclose_in_block: None, - }) - }); + let (lit, _) = + self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| { + self_.sess.create_err(UnexpectedTokenAfterLabel { + span: self_.token.span, + remove_label: None, + enclose_in_block: None, + }) + }); consume_colon = false; - Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit))) + Ok(self.mk_expr(lo, ExprKind::Lit(lit))) } else if !ate_colon && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { @@ -1581,7 +1595,7 @@ impl<'a> Parser<'a> { vis.0 }; - // Suggestion involves adding a (as of time of writing this, unstable) labeled block. + // Suggestion involves adding a labeled block. // // If there are no breaks that may use this label, suggest removing the label and // recover to the unmodified expression. @@ -1617,12 +1631,13 @@ impl<'a> Parser<'a> { Ok(expr) } - /// Emit an error when a char is parsed as a lifetime because of a missing quote - pub(super) fn recover_unclosed_char( + /// Emit an error when a char is parsed as a lifetime because of a missing quote. + pub(super) fn recover_unclosed_char<L>( &self, lifetime: Ident, + mk_lit_char: impl FnOnce(Symbol, Span) -> L, err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, - ) -> ast::MetaItemLit { + ) -> L { if let Some(mut diag) = self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) { @@ -1644,11 +1659,7 @@ impl<'a> Parser<'a> { .emit(); } let name = lifetime.without_first_quote().name; - ast::MetaItemLit { - token_lit: token::Lit::new(token::LitKind::Char, name, None), - kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')), - span: lifetime.span, - } + mk_lit_char(name, lifetime.span) } /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. @@ -1703,10 +1714,10 @@ impl<'a> Parser<'a> { fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; let mut label = self.eat_label(); - let kind = if label.is_some() && self.token == token::Colon { + let kind = if self.token == token::Colon && let Some(label) = label.take() { // The value expression can be a labeled loop, see issue #86948, e.g.: // `loop { break 'label: loop { break 'label 42; }; }` - let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?; + let lexpr = self.parse_labeled_expr(label, true)?; self.sess.emit_err(LabeledLoopInBreak { span: lexpr.span, sub: WrapExpressionInParentheses { @@ -1718,8 +1729,8 @@ impl<'a> Parser<'a> { } else if self.token != token::OpenDelim(Delimiter::Brace) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { - let expr = self.parse_expr_opt()?; - if let Some(expr) = &expr { + let mut expr = self.parse_expr_opt()?; + if let Some(expr) = &mut expr { if label.is_some() && matches!( expr.kind, @@ -1737,7 +1748,19 @@ impl<'a> Parser<'a> { BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span), ); } + + // Recover `break label aaaaa` + if self.may_recover() + && let ExprKind::Path(None, p) = &expr.kind + && let [segment] = &*p.segments + && let &ast::PathSegment { ident, args: None, .. } = segment + && let Some(next) = self.parse_expr_opt()? + { + label = Some(self.recover_ident_into_label(ident)); + *expr = next; + } } + expr } else { None @@ -1746,6 +1769,23 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } + /// Parse `"continue" label?`. + fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> { + let mut label = self.eat_label(); + + // Recover `continue label` -> `continue 'label` + if self.may_recover() + && label.is_none() + && let Some((ident, _)) = self.token.ident() + { + self.bump(); + label = Some(self.recover_ident_into_label(ident)); + } + + let kind = ExprKind::Continue(label); + Ok(self.mk_expr(lo.to(self.prev_token.span), kind)) + } + /// Parse `"yield" expr?`. fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; @@ -1764,8 +1804,8 @@ impl<'a> Parser<'a> { Some(lit) => match lit.kind { ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { style, - symbol: lit.token_lit.symbol, - suffix: lit.token_lit.suffix, + symbol: lit.symbol, + suffix: lit.suffix, span: lit.span, symbol_unescaped, }), @@ -1775,7 +1815,23 @@ impl<'a> Parser<'a> { } } - fn handle_missing_lit(&mut self) -> PResult<'a, MetaItemLit> { + pub(crate) fn mk_token_lit_char(name: Symbol, span: Span) -> (token::Lit, Span) { + (token::Lit { symbol: name, suffix: None, kind: token::Char }, span) + } + + fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit { + ast::MetaItemLit { + symbol: name, + suffix: None, + kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')), + span, + } + } + + fn handle_missing_lit<L>( + &mut self, + mk_lit_char: impl FnOnce(Symbol, Span) -> L, + ) -> PResult<'a, L> { if let token::Interpolated(inner) = &self.token.kind { let expr = match inner.as_ref() { token::NtExpr(expr) => Some(expr), @@ -1799,7 +1855,7 @@ impl<'a> Parser<'a> { // On an error path, eagerly consider a lifetime to be an unclosed character lit if self.token.is_lifetime() { let lt = self.expect_lifetime(); - Ok(self.recover_unclosed_char(lt.ident, err)) + Ok(self.recover_unclosed_char(lt.ident, mk_lit_char, err)) } else { Err(err(self)) } @@ -1808,11 +1864,13 @@ impl<'a> Parser<'a> { pub(super) fn parse_token_lit(&mut self) -> PResult<'a, (token::Lit, Span)> { self.parse_opt_token_lit() .ok_or(()) - .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span))) + .or_else(|()| self.handle_missing_lit(Parser::mk_token_lit_char)) } pub(super) fn parse_meta_item_lit(&mut self) -> PResult<'a, MetaItemLit> { - self.parse_opt_meta_item_lit().ok_or(()).or_else(|()| self.handle_missing_lit()) + self.parse_opt_meta_item_lit() + .ok_or(()) + .or_else(|()| self.handle_missing_lit(Parser::mk_meta_item_lit_char)) } fn recover_after_dot(&mut self) -> Option<Token> { @@ -2015,7 +2073,7 @@ impl<'a> Parser<'a> { }); } - let (attrs, blk) = self.parse_block_common(lo, blk_mode)?; + let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?; Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } @@ -2041,6 +2099,8 @@ impl<'a> Parser<'a> { ClosureBinder::NotPresent }; + let constness = self.parse_constness(Case::Sensitive); + let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; @@ -2087,6 +2147,7 @@ impl<'a> Parser<'a> { ExprKind::Closure(Box::new(ast::Closure { binder, capture_clause, + constness, asyncness, movability, fn_decl, @@ -3013,9 +3074,29 @@ impl<'a> Parser<'a> { false } + /// Converts an ident into 'label and emits an "expected a label, found an identifier" error. + fn recover_ident_into_label(&mut self, ident: Ident) -> Label { + // Convert `label` -> `'label`, + // so that nameres doesn't complain about non-existing label + let label = format!("'{}", ident.name); + let ident = Ident { name: Symbol::intern(&label), span: ident.span }; + + self.struct_span_err(ident.span, "expected a label, found an identifier") + .span_suggestion( + ident.span, + "labels start with a tick", + label, + Applicability::MachineApplicable, + ) + .emit(); + + Label { ident } + } + /// Parses `ident (COLON expr)?`. fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; + self.recover_diff_marker(); self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; |