cast_expr: P<Expr>,
) -> PResult<'a, P<Expr>> {
let span = cast_expr.span;
- let maybe_ascription_span = if let ExprKind::Type(ascripted_expr, _) = &cast_expr.kind {
- Some(ascripted_expr.span.shrink_to_hi().with_hi(span.hi()))
- } else {
- None
- };
+ let (cast_kind, maybe_ascription_span) =
+ if let ExprKind::Type(ascripted_expr, _) = &cast_expr.kind {
+ ("type ascription", Some(ascripted_expr.span.shrink_to_hi().with_hi(span.hi())))
+ } else {
+ ("cast", None)
+ };
// Save the memory location of expr before parsing any following postfix operators.
// This will be compared with the memory location of the output expression.
// If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator.
if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed {
let msg = format!(
- "casts cannot be followed by {}",
+ "{cast_kind} cannot be followed by {}",
match with_postfix.kind {
ExprKind::Index(_, _) => "indexing",
ExprKind::Try(_) => "`?`",
} else if self.is_do_yeet() {
self.parse_yeet_expr(attrs)
} else if self.check_keyword(kw::Let) {
- self.manage_let_chains_context();
- self.bump();
self.parse_let_expr(attrs)
} else if self.eat_keyword(kw::Underscore) {
Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
/// Parses the condition of a `if` or `while` expression.
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
- self.with_let_management(true, |local_self| {
- local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)
- })
+ self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)
}
- // Checks if `let` is in an invalid position like `let x = let y = 1;` or
- // if the current `let` is in a let_chains context but nested in another
- // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`.
- //
- // This method expects that the current token is `let`.
- fn manage_let_chains_context(&mut self) {
- debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _)));
- let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed
- && !matches!(
- self.prev_token.kind,
- TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
- );
- if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr {
+ /// Parses a `let $pat = $expr` pseudo-expression.
+ fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ // This is a *approximate* heuristic that detects if `let` chains are
+ // being parsed in the right position. It's approximate because it
+ // doesn't deny all invalid `let` expressions, just completely wrong usages.
+ let not_in_chain = !matches!(
+ self.prev_token.kind,
+ TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
+ );
+ if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
self.struct_span_err(self.token.span, "expected expression, found `let` statement")
.emit();
}
- }
- /// Parses a `let $pat = $expr` pseudo-expression.
- /// The `let` token has already been eaten.
- fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ self.bump(); // Eat `let` token
let lo = self.prev_token.span;
let pat = self.parse_pat_allow_top_alt(
None,
// `&&` tokens.
fn check_let_expr(expr: &Expr) -> bool {
match expr.kind {
- ExprKind::Binary(_, ref lhs, ref rhs) => check_let_expr(lhs) || check_let_expr(rhs),
+ ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, ref lhs, ref rhs) => {
+ check_let_expr(lhs) || check_let_expr(rhs)
+ }
ExprKind::Let(..) => true,
_ => false,
}
)?;
let guard = if this.eat_keyword(kw::If) {
let if_span = this.prev_token.span;
- let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?;
- let has_let_expr = check_let_expr(&cond);
- if has_let_expr {
+ let cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
+ if check_let_expr(&cond) {
let span = if_span.to(cond.span);
this.sess.gated_spans.gate(sym::if_let_guard, span);
}
Ok((res, trailing))
})
}
-
- // Calls `f` with the internal `let_expr_allowed` set to `let_expr_allowed` and then
- // sets the internal `let_expr_allowed` back to its original value.
- fn with_let_management<T>(
- &mut self,
- let_expr_allowed: bool,
- f: impl FnOnce(&mut Self) -> T,
- ) -> T {
- let last_let_expr_allowed = mem::replace(&mut self.let_expr_allowed, let_expr_allowed);
- let rslt = f(self);
- self.let_expr_allowed = last_let_expr_allowed;
- rslt
- }
}