diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 124 | 
1 files changed, 83 insertions, 41 deletions
| diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 00947a4c585..1b99bc015b6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3,7 +3,7 @@ use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ - AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Recovered, Restrictions, + AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken, }; @@ -11,7 +11,7 @@ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; use ast::token::IdentIsRaw; -use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered}; use core::mem; use core::ops::ControlFlow; use rustc_ast::ptr::P; @@ -36,6 +36,7 @@ use rustc_span::source_map::{self, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span}; use thin_vec::{thin_vec, ThinVec}; +use tracing::instrument; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -45,7 +46,7 @@ use thin_vec::{thin_vec, ThinVec}; macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = &$p.token.kind { - match &nt.0 { + match &**nt { token::NtExpr(e) | token::NtLiteral(e) => { let e = e.clone(); $p.bump(); @@ -327,7 +328,9 @@ impl<'a> Parser<'a> { this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); + self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + let span = lhs_span.to(rhs.span); + lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -426,6 +429,18 @@ impl<'a> Parser<'a> { }); } + fn error_ambiguous_outer_attrs(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) { + if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { + self.dcx().emit_err(errors::AmbiguousOuterAttributes { + span: attr.span.to(rhs_span), + sugg: errors::WrapInParentheses::Expression { + left: attr.span.shrink_to_lo(), + right: lhs_span.shrink_to_hi(), + }, + }); + } + } + /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// @@ -483,8 +498,7 @@ impl<'a> Parser<'a> { /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) - && !classify::expr_requires_semi_to_be_stmt(e) + self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e) } /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. @@ -506,7 +520,8 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); + self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); + let span = lhs.span.to(rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -710,7 +725,9 @@ impl<'a> Parser<'a> { /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, + TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) | TokenKind::Interpolated(..) => { + self.prev_token.span + } _ => expr.span, } } @@ -722,7 +739,8 @@ impl<'a> Parser<'a> { expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind, ) -> PResult<'a, P<Expr>> { let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) + this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -979,6 +997,12 @@ impl<'a> Parser<'a> { // we are using noexpect here because we don't expect a `.` directly after a `return` // which could be suggested otherwise self.eat_noexpect(&token::Dot) + } else if self.token.kind == TokenKind::RArrow && self.may_recover() { + // Recovery for `expr->suffix`. + self.bump(); + let span = self.prev_token.span; + self.dcx().emit_err(errors::ExprRArrowCall { span }); + true } else { self.eat(&token::Dot) }; @@ -1890,11 +1914,10 @@ impl<'a> Parser<'a> { | ExprKind::Block(_, None) ) { - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression", BuiltinLintDiag::BreakWithLabelAndLoop(expr.span), ); } @@ -2607,7 +2630,7 @@ impl<'a> Parser<'a> { CondChecker::new(self).visit_expr(&mut cond); - if let ExprKind::Let(_, _, _, None) = cond.kind { + if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { // Remove the last feature gating of a `let` expression since it's stable. self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); } @@ -2617,7 +2640,7 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> { - let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { + let recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { let err = errors::ExpectedExpressionFoundLet { span: self.token.span, reason: ForbiddenLetReason::OtherForbidden, @@ -2628,10 +2651,10 @@ impl<'a> Parser<'a> { // This was part of a closure, the that part of the parser recover. return Err(self.dcx().create_err(err)); } else { - Some(self.dcx().emit_err(err)) + Recovered::Yes(self.dcx().emit_err(err)) } } else { - None + Recovered::No }; self.bump(); // Eat `let` token let lo = self.prev_token.span; @@ -2652,7 +2675,7 @@ impl<'a> Parser<'a> { } let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; let span = lo.to(expr.span); - Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, is_recovered))) + Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } /// Parses an `else { ... }` expression (`else` token already eaten). @@ -2669,8 +2692,33 @@ impl<'a> Parser<'a> { let first_tok_span = self.token.span; match self.parse_expr() { Ok(cond) - // If it's not a free-standing expression, and is followed by a block, - // then it's very likely the condition to an `else if`. + // Try to guess the difference between a "condition-like" vs + // "statement-like" expression. + // + // We are seeing the following code, in which $cond is neither + // ExprKind::Block nor ExprKind::If (the 2 cases wherein this + // would be valid syntax). + // + // if ... { + // } else $cond + // + // If $cond is "condition-like" such as ExprKind::Binary, we + // want to suggest inserting `if`. + // + // if ... { + // } else if a == b { + // ^^ + // } + // + // If $cond is "statement-like" such as ExprKind::While then we + // want to suggest wrapping in braces. + // + // if ... { + // } else { + // ^ + // while true {} + // } + // ^ if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) && classify::expr_requires_semi_to_be_stmt(&cond) => { @@ -2976,7 +3024,7 @@ impl<'a> Parser<'a> { &mut self, first_expr: &P<Expr>, arrow_span: Span, - ) -> Option<P<Expr>> { + ) -> Option<(Span, ErrorGuaranteed)> { if self.token.kind != token::Semi { return None; } @@ -3001,7 +3049,7 @@ impl<'a> Parser<'a> { errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } }, }); - this.mk_expr_err(span, guar) + (span, guar) }; // We might have either a `,` -> `;` typo, or a block without braces. We need // a more subtle parsing strategy. @@ -3114,16 +3162,19 @@ impl<'a> Parser<'a> { err })?; - let require_comma = classify::expr_requires_semi_to_be_stmt(&expr) + let require_comma = !classify::expr_is_complete(&expr) && this.token != token::CloseDelim(Delimiter::Brace); if !require_comma { arm_body = Some(expr); this.eat(&token::Comma); Ok(Recovered::No) - } else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) { + } else if let Some((span, guar)) = + this.parse_arm_body_missing_braces(&expr, arrow_span) + { + let body = this.mk_expr_err(span, guar); arm_body = Some(body); - Ok(Recovered::Yes) + Ok(Recovered::Yes(guar)) } else { let expr_span = expr.span; arm_body = Some(expr); @@ -3201,10 +3252,10 @@ impl<'a> Parser<'a> { .is_ok(); if pattern_follows && snapshot.check(&TokenKind::FatArrow) { err.cancel(); - this.dcx().emit_err(errors::MissingCommaAfterMatchArm { + let guar = this.dcx().emit_err(errors::MissingCommaAfterMatchArm { span: arm_span.shrink_to_hi(), }); - return Ok(Recovered::Yes); + return Ok(Recovered::Yes(guar)); } Err(err) }); @@ -3683,7 +3734,7 @@ impl<'a> Parser<'a> { /// Parses `ident (COLON expr)?`. fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; - self.recover_diff_marker(); + self.recover_vcs_conflict_marker(); self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -3807,16 +3858,6 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } - /// Create expression span ensuring the span of the parent node - /// is larger than the span of lhs and rhs, including the attributes. - fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span { - lhs.attrs - .iter() - .find(|a| a.style == AttrStyle::Outer) - .map_or(lhs_span, |a| a.span) - .to(rhs_span) - } - fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, @@ -3892,15 +3933,16 @@ impl MutVisitor for CondChecker<'_> { let span = e.span; match e.kind { - ExprKind::Let(_, _, _, ref mut is_recovered @ None) => { + ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { - *is_recovered = - Some(self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { + *recovered = Recovered::Yes(self.parser.dcx().emit_err( + errors::ExpectedExpressionFoundLet { span, reason, missing_let: self.missing_let, comparison: self.comparison, - })); + }, + )); } else { self.parser.psess.gated_spans.gate(sym::let_chains, span); } @@ -3968,7 +4010,7 @@ impl MutVisitor for CondChecker<'_> { self.visit_expr(op); self.forbid_let_reason = forbid_let_reason; } - ExprKind::Let(_, _, _, Some(_)) + ExprKind::Let(_, _, _, Recovered::Yes(_)) | ExprKind::Array(_) | ExprKind::ConstBlock(_) | ExprKind::Lit(_) | 
