summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs239
1 files changed, 169 insertions, 70 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index f4cee3a66..235b28b6e 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -9,7 +9,7 @@ use super::{
use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath;
use ast::mut_visit::{noop_visit_expr, MutVisitor};
-use ast::{Path, PathSegment};
+use ast::{GenBlockKind, Path, PathSegment};
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -32,10 +32,10 @@ use rustc_macros::Subdiagnostic;
use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics;
-use rustc_span::source_map::{self, Span, Spanned};
+use rustc_span::source_map::{self, Spanned};
use rustc_span::symbol::kw::PathRoot;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{BytePos, Pos};
+use rustc_span::{BytePos, Pos, Span};
use thin_vec::{thin_vec, ThinVec};
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
@@ -1007,8 +1007,9 @@ impl<'a> Parser<'a> {
let span = self.token.span;
let sm = self.sess.source_map();
let (span, actual) = match (&self.token.kind, self.subparser_name) {
- (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) =>
- (span.shrink_to_hi(), actual.into()),
+ (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => {
+ (span.shrink_to_hi(), actual.into())
+ }
_ => (span, actual),
};
self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual });
@@ -1440,14 +1441,20 @@ impl<'a> Parser<'a> {
} else if this.token.uninterpolated_span().at_least_rust_2018() {
// `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
if this.check_keyword(kw::Async) {
- if this.is_async_block() {
+ if this.is_gen_block(kw::Async) {
// Check for `async {` and `async move {`.
- this.parse_async_block()
+ this.parse_gen_block()
} else {
this.parse_expr_closure()
}
} else if this.eat_keyword(kw::Await) {
this.recover_incorrect_await_syntax(lo, this.prev_token.span)
+ } else if this.token.uninterpolated_span().at_least_rust_2024() {
+ if this.is_gen_block(kw::Gen) {
+ this.parse_gen_block()
+ } else {
+ this.parse_expr_lit()
+ }
} else {
this.parse_expr_lit()
}
@@ -1550,10 +1557,7 @@ impl<'a> Parser<'a> {
self.sess.emit_err(errors::MacroInvocationWithQualifiedPath(path.span));
}
let lo = path.span;
- let mac = P(MacCall {
- path,
- args: self.parse_delim_args()?,
- });
+ let mac = P(MacCall { path, args: self.parse_delim_args()? });
(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)
@@ -1592,7 +1596,7 @@ impl<'a> Parser<'a> {
} else if !ate_colon
&& self.may_recover()
&& (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
- || self.token.is_op())
+ || self.token.is_punct())
{
let (lit, _) =
self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {
@@ -1771,7 +1775,9 @@ impl<'a> Parser<'a> {
fn parse_expr_break(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let mut label = self.eat_label();
- let kind = if self.token == token::Colon && let Some(label) = label.take() {
+ 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_expr_labeled(label, true)?;
@@ -1848,7 +1854,7 @@ impl<'a> Parser<'a> {
let lo = self.prev_token.span;
let kind = ExprKind::Yield(self.parse_expr_opt()?);
let span = lo.to(self.prev_token.span);
- self.sess.gated_spans.gate(sym::generators, span);
+ self.sess.gated_spans.gate(sym::yield_expr, span);
let expr = self.mk_expr(span, kind);
self.maybe_recover_from_bad_qpath(expr)
}
@@ -2209,6 +2215,7 @@ impl<'a> Parser<'a> {
fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
+ let before = self.prev_token.clone();
let binder = if self.check_keyword(kw::For) {
let lo = self.token.span;
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
@@ -2239,7 +2246,12 @@ impl<'a> Parser<'a> {
FnRetTy::Default(_) => {
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
- self.parse_expr_res(restrictions, None)?
+ let prev = self.prev_token.clone();
+ let token = self.token.clone();
+ match self.parse_expr_res(restrictions, None) {
+ Ok(expr) => expr,
+ Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
+ }
}
_ => {
// If an explicit return type is given, require a block to appear (RFC 968).
@@ -2291,13 +2303,14 @@ impl<'a> Parser<'a> {
/// Parses an optional `move` prefix to a closure-like construct.
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
if self.eat_keyword(kw::Move) {
+ let move_kw_span = self.prev_token.span;
// Check for `move async` and recover
if self.check_keyword(kw::Async) {
let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
Err(errors::AsyncMoveOrderIncorrect { span: move_async_span }
.into_diagnostic(&self.sess.span_diagnostic))
} else {
- Ok(CaptureBy::Value)
+ Ok(CaptureBy::Value { move_kw: move_kw_span })
}
} else {
Ok(CaptureBy::Ref)
@@ -2371,16 +2384,18 @@ impl<'a> Parser<'a> {
let mut recover_block_from_condition = |this: &mut Self| {
let block = match &mut cond.kind {
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
- if let ExprKind::Block(_, None) = right.kind => {
- self.sess.emit_err(errors::IfExpressionMissingThenBlock {
- if_span: lo,
- missing_then_block_sub:
- errors::IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
- let_else_sub: None,
-
- });
- std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
- },
+ if let ExprKind::Block(_, None) = right.kind =>
+ {
+ self.sess.emit_err(errors::IfExpressionMissingThenBlock {
+ if_span: lo,
+ missing_then_block_sub:
+ errors::IfExpressionMissingThenBlockSub::UnfinishedCondition(
+ cond_span.shrink_to_lo().to(*binop_span),
+ ),
+ let_else_sub: None,
+ });
+ std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
+ }
ExprKind::Block(_, None) => {
self.sess.emit_err(errors::IfExpressionMissingCondition {
if_span: lo.shrink_to_hi(),
@@ -2426,10 +2441,26 @@ impl<'a> Parser<'a> {
self.error_on_extra_if(&cond)?;
// Parse block, which will always fail, but we can add a nice note to the error
self.parse_block().map_err(|mut err| {
- err.span_note(
- cond_span,
- "the `if` expression is missing a block after this condition",
- );
+ if self.prev_token == token::Semi
+ && self.token == token::AndAnd
+ && let maybe_let = self.look_ahead(1, |t| t.clone())
+ && maybe_let.is_keyword(kw::Let)
+ {
+ err.span_suggestion(
+ self.prev_token.span,
+ "consider removing this semicolon to parse the `let` as part of the same chain",
+ "",
+ Applicability::MachineApplicable,
+ ).span_note(
+ self.token.span.to(maybe_let.span),
+ "you likely meant to continue parsing the let-chain starting here",
+ );
+ } else {
+ err.span_note(
+ cond_span,
+ "the `if` expression is missing a block after this condition",
+ );
+ }
err
})?
}
@@ -2459,10 +2490,16 @@ 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) {
- Some(self.sess.emit_err(errors::ExpectedExpressionFoundLet {
+ let err = errors::ExpectedExpressionFoundLet {
span: self.token.span,
reason: ForbiddenLetReason::OtherForbidden,
- }))
+ };
+ if self.prev_token.kind == token::BinOp(token::Or) {
+ // This was part of a closure, the that part of the parser recover.
+ return Err(err.into_diagnostic(&self.sess.span_diagnostic));
+ } else {
+ Some(self.sess.emit_err(err))
+ }
} else {
None
};
@@ -2557,13 +2594,16 @@ impl<'a> Parser<'a> {
}
fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> {
- if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind &&
- let BinOpKind::And = binop &&
- let ExprKind::If(cond, ..) = &right.kind {
- Err(self.sess.create_err(errors::UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
- } else {
- Ok(())
- }
+ if let ExprKind::Binary(Spanned { span: binop_span, node: binop }, _, right) = &cond.kind
+ && let BinOpKind::And = binop
+ && let ExprKind::If(cond, ..) = &right.kind
+ {
+ Err(self.sess.create_err(errors::UnexpectedIfWithIf(
+ binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()),
+ )))
+ } else {
+ Ok(())
+ }
}
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
@@ -2834,7 +2874,7 @@ impl<'a> Parser<'a> {
)?;
let guard = if this.eat_keyword(kw::If) {
let if_span = this.prev_token.span;
- let mut cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
+ let mut cond = this.parse_match_guard_condition()?;
CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
@@ -2860,9 +2900,9 @@ impl<'a> Parser<'a> {
{
err.span_suggestion(
this.token.span,
- "try using a fat arrow here",
+ "use a fat arrow to start a match arm",
"=>",
- Applicability::MaybeIncorrect,
+ Applicability::MachineApplicable,
);
err.emit();
this.bump();
@@ -2911,9 +2951,9 @@ impl<'a> Parser<'a> {
.or_else(|mut err| {
if this.token == token::FatArrow {
if let Ok(expr_lines) = sm.span_to_lines(expr.span)
- && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span)
- && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col
- && expr_lines.lines.len() == 2
+ && let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span)
+ && arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col
+ && expr_lines.lines.len() == 2
{
// We check whether there's any trailing code in the parse span,
// if there isn't, we very likely have the following:
@@ -2979,6 +3019,33 @@ impl<'a> Parser<'a> {
})
}
+ fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
+ self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err(
+ |mut err| {
+ if self.prev_token == token::OpenDelim(Delimiter::Brace) {
+ let sugg_sp = self.prev_token.span.shrink_to_lo();
+ // Consume everything within the braces, let's avoid further parse
+ // errors.
+ self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
+ let msg = "you might have meant to start a match arm after the match guard";
+ if self.eat(&token::CloseDelim(Delimiter::Brace)) {
+ let applicability = if self.token.kind != token::FatArrow {
+ // We have high confidence that we indeed didn't have a struct
+ // literal in the match guard, but rather we had some operation
+ // that ended in a path, immediately followed by a block that was
+ // meant to be the match arm.
+ Applicability::MachineApplicable
+ } else {
+ Applicability::MaybeIncorrect
+ };
+ err.span_suggestion_verbose(sugg_sp, msg, "=> ".to_string(), applicability);
+ }
+ }
+ err
+ },
+ )
+ }
+
pub(crate) fn is_builtin(&self) -> bool {
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
}
@@ -3015,18 +3082,24 @@ impl<'a> Parser<'a> {
&& self.token.uninterpolated_span().at_least_rust_2018()
}
- /// Parses an `async move? {...}` expression.
- fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> {
+ /// Parses an `async move? {...}` or `gen move? {...}` expression.
+ fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
- self.expect_keyword(kw::Async)?;
+ let kind = if self.eat_keyword(kw::Async) {
+ GenBlockKind::Async
+ } else {
+ assert!(self.eat_keyword(kw::Gen));
+ self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span));
+ GenBlockKind::Gen
+ };
let capture_clause = self.parse_capture_clause()?;
let (attrs, body) = self.parse_inner_attrs_and_block()?;
- let kind = ExprKind::Async(capture_clause, body);
+ let kind = ExprKind::Gen(capture_clause, body, kind);
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
}
- fn is_async_block(&self) -> bool {
- self.token.is_keyword(kw::Async)
+ fn is_gen_block(&self, kw: Symbol) -> bool {
+ self.token.is_keyword(kw)
&& ((
// `async move {`
self.is_keyword_ahead(1, &[kw::Move])
@@ -3049,9 +3122,10 @@ impl<'a> Parser<'a> {
|| self.look_ahead(2, |t| t == &token::Colon)
&& (
// `{ ident: token, ` cannot start a block.
- self.look_ahead(4, |t| t == &token::Comma) ||
- // `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
- self.look_ahead(3, |t| !t.can_begin_type())
+ self.look_ahead(4, |t| t == &token::Comma)
+ // `{ ident: ` cannot start a block unless it's a type ascription
+ // `ident: Type`.
+ || self.look_ahead(3, |t| !t.can_begin_type())
)
)
}
@@ -3091,6 +3165,7 @@ impl<'a> Parser<'a> {
let mut fields = ThinVec::new();
let mut base = ast::StructRest::None;
let mut recover_async = false;
+ let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
let mut async_block_err = |e: &mut Diagnostic, span: Span| {
recover_async = true;
@@ -3128,6 +3203,26 @@ impl<'a> Parser<'a> {
e.span_label(pth.span, "while parsing this struct");
}
+ if let Some((ident, _)) = self.token.ident()
+ && !self.token.is_reserved_ident()
+ && self.look_ahead(1, |t| {
+ AssocOp::from_token(&t).is_some()
+ || matches!(t.kind, token::OpenDelim(_))
+ || t.kind == token::Dot
+ })
+ {
+ // Looks like they tried to write a shorthand, complex expression.
+ e.span_suggestion_verbose(
+ self.token.span.shrink_to_lo(),
+ "try naming a field",
+ &format!("{ident}: ",),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ if in_if_guard && close_delim == Delimiter::Brace {
+ return Err(e);
+ }
+
if !recover {
return Err(e);
}
@@ -3173,19 +3268,6 @@ impl<'a> Parser<'a> {
",",
Applicability::MachineApplicable,
);
- } else if is_shorthand
- && (AssocOp::from_token(&self.token).is_some()
- || matches!(&self.token.kind, token::OpenDelim(_))
- || self.token.kind == token::Dot)
- {
- // Looks like they tried to write a shorthand, complex expression.
- let ident = parsed_field.expect("is_shorthand implies Some").ident;
- e.span_suggestion(
- ident.span.shrink_to_lo(),
- "try naming a field",
- &format!("{ident}: "),
- Applicability::HasPlaceholders,
- );
}
}
if !recover {
@@ -3288,6 +3370,24 @@ impl<'a> Parser<'a> {
// Check if a colon exists one ahead. This means we're parsing a fieldname.
let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
+ // Proactively check whether parsing the field will be incorrect.
+ let is_wrong = this.token.is_ident()
+ && !this.token.is_reserved_ident()
+ && !this.look_ahead(1, |t| {
+ t == &token::Colon
+ || t == &token::Eq
+ || t == &token::Comma
+ || t == &token::CloseDelim(Delimiter::Brace)
+ || t == &token::CloseDelim(Delimiter::Parenthesis)
+ });
+ if is_wrong {
+ return Err(errors::ExpectedStructField {
+ span: this.look_ahead(1, |t| t.span),
+ ident_span: this.token.span,
+ token: this.look_ahead(1, |t| t.clone()),
+ }
+ .into_diagnostic(&self.sess.span_diagnostic));
+ }
let (ident, expr) = if is_shorthand {
// Mimic `x: x` for the `x` field shorthand.
let ident = this.parse_ident_common(false)?;
@@ -3508,8 +3608,7 @@ impl MutVisitor for CondChecker<'_> {
noop_visit_expr(e, self);
self.forbid_let_reason = forbid_let_reason;
}
- ExprKind::Cast(ref mut op, _)
- | ExprKind::Type(ref mut op, _) => {
+ ExprKind::Cast(ref mut op, _) | ExprKind::Type(ref mut op, _) => {
let forbid_let_reason = self.forbid_let_reason;
self.forbid_let_reason = Some(OtherForbidden);
self.visit_expr(op);
@@ -3526,7 +3625,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::Match(_, _)
| ExprKind::Closure(_)
| ExprKind::Block(_, _)
- | ExprKind::Async(_, _)
+ | ExprKind::Gen(_, _, _)
| ExprKind::TryBlock(_)
| ExprKind::Underscore
| ExprKind::Path(_, _)