compiler/rustc_parse/src/parser/expr.rs RUST 4,542 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 4,542.
1// ignore-tidy-file-filelength23use core::mem;4use core::ops::{Bound, ControlFlow};56use ast::mut_visit::{self, MutVisitor};7use ast::token::IdentIsRaw;8use ast::{ForLoopKind, MatchKind, Pat, Path, PathSegment, Recovered};9use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind};10use rustc_ast::util::case::Case;11use rustc_ast::util::classify;12use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};13use rustc_ast::visit::{Visitor, walk_expr};14use rustc_ast::{15    self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,16    BlockCheckMode, CaptureBy, ClosureBinder, CoroutineKind, DUMMY_NODE_ID, Expr, ExprField,17    ExprKind, FnDecl, FnRetTy, ForLoop, Guard, Label, MacCall, MetaItemLit, Movability, Param,18    RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind,19};20use rustc_ast_pretty::pprust;21use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};22use rustc_lint_defs::builtin::BREAK_WITH_LABEL_AND_LOOP;23use rustc_literal_escaper::unescape_char;24use rustc_session::diagnostics::report_lit_error;25use rustc_span::edition::Edition;26use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw, respan, sym};27use thin_vec::{ThinVec, thin_vec};28use tracing::instrument;2930use super::diagnostics::SnapshotParser;31use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};32use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};33use super::{34    AttrWrapper, BlockMode, ClosureSpans, ExpTokenPair, ForceCollect, Parser, PathStyle,35    Restrictions, SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos,36};37use crate::diagnostics::ExprParenthesesNeeded;38use crate::{diagnostics, exp, maybe_recover_from_interpolated_ty_qpath};3940#[derive(Debug)]41pub(super) enum DestructuredFloat {42    /// 1e243    Single(Symbol, Span),44    /// 1.45    TrailingDot(Symbol, Span, Span),46    /// 1.2 | 1.2e347    MiddleDot(Symbol, Span, Span, Symbol, Span),48    /// Invalid49    Error,50}5152impl<'a> Parser<'a> {53    /// Parses an expression.54    #[inline]55    pub fn parse_expr(&mut self) -> PResult<'a, Box<Expr>> {56        self.current_closure.take();57        self.parse_expr_res(Restrictions::empty())58    }5960    /// Parses an expression, forcing tokens to be collected.61    pub fn parse_expr_force_collect(&mut self) -> PResult<'a, Box<Expr>> {62        self.current_closure.take();6364        // If the expression is associative (e.g. `1 + 2`), then any preceding65        // outer attribute actually belongs to the first inner sub-expression.66        // In which case we must use the pre-attr pos to include the attribute67        // in the collected tokens for the outer expression.68        let pre_attr_pos = self.collect_pos();69        let attrs = self.parse_outer_attributes()?;70        self.collect_tokens(71            Some(pre_attr_pos),72            AttrWrapper::empty(),73            ForceCollect::Yes,74            |this, _empty_attrs| {75                let (expr, is_assoc) =76                    this.parse_expr_res_after_attrs(Restrictions::empty(), attrs)?;77                let use_pre_attr_pos =78                    if is_assoc { UsePreAttrPos::Yes } else { UsePreAttrPos::No };79                Ok((expr, Trailing::No, use_pre_attr_pos))80            },81        )82    }8384    pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> {85        self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value })86    }8788    fn parse_expr_catch_underscore(89        &mut self,90        restrictions: Restrictions,91    ) -> PResult<'a, Box<Expr>> {92        match self.parse_expr_res(restrictions) {93            Ok(expr) => Ok(expr),94            Err(err) => match self.token.ident() {95                Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No))96                    if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>97                {98                    // Special-case handling of `foo(_, _, _)`99                    let guar = err.emit();100                    self.bump();101                    Ok(self.mk_expr(self.prev_token.span, ExprKind::Err(guar)))102                }103                _ => Err(err),104            },105        }106    }107108    /// Parses a sequence of expressions delimited by parentheses.109    fn parse_expr_paren_seq(&mut self) -> PResult<'a, ThinVec<Box<Expr>>> {110        self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore(Restrictions::empty()))111            .map(|(r, _)| r)112    }113114    /// Parses an expression, subject to the given restrictions.115    #[inline]116    pub(super) fn parse_expr_res(&mut self, r: Restrictions) -> PResult<'a, Box<Expr>> {117        let attrs = self.parse_outer_attributes()?;118        self.parse_expr_res_after_attrs(r, attrs).map(|(expr, _)| expr)119    }120121    /// Same as `parse_expr_res`, but with attributes already pre-parsed.122    /// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator123    /// followed by a subexpression (e.g. `1 + 2`).124    #[inline]125    pub(super) fn parse_expr_res_after_attrs(126        &mut self,127        r: Restrictions,128        attrs: AttrWrapper,129    ) -> PResult<'a, (Box<Expr>, bool)> {130        self.with_res(r, |this| this.parse_expr_assoc_after_attrs(Bound::Unbounded, attrs))131    }132133    /// Parses an associative expression with operators of at least `min_prec` precedence.134    pub(super) fn parse_expr_assoc(135        &mut self,136        min_prec: Bound<ExprPrecedence>,137    ) -> PResult<'a, Box<Expr>> {138        let attrs = self.parse_outer_attributes()?;139        self.parse_expr_assoc_after_attrs(min_prec, attrs).map(|(expr, _)| expr)140    }141142    /// Same as `parse_expr_assoc`, but with attributes already pre-parsed.143    /// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator144    /// followed by a subexpression (e.g. `1 + 2`).145    pub(super) fn parse_expr_assoc_after_attrs(146        &mut self,147        min_prec: Bound<ExprPrecedence>,148        attrs: AttrWrapper,149    ) -> PResult<'a, (Box<Expr>, bool)> {150        let lhs = if self.token.is_range_separator() {151            return self.parse_expr_prefix_range(attrs).map(|res| (res, false));152        } else {153            self.parse_expr_prefix(attrs)?154        };155        self.parse_expr_assoc_rest(min_prec, false, lhs)156    }157158    /// Parses the rest of an associative expression (i.e. the part after the lhs) with operators159    /// of at least `min_prec` precedence. The `bool` in the return value indicates if something160    /// was actually parsed.161    pub(super) fn parse_expr_assoc_rest(162        &mut self,163        min_prec: Bound<ExprPrecedence>,164        starts_stmt: bool,165        mut lhs: Box<Expr>,166    ) -> PResult<'a, (Box<Expr>, bool)> {167        let mut parsed_something = false;168        if !self.should_continue_as_assoc_expr(&lhs) {169            return Ok((lhs, parsed_something));170        }171172        self.expected_token_types.insert(TokenType::Operator);173        while let Some(op) = self.check_assoc_op() {174            let lhs_span = self.interpolated_or_expr_span(&lhs);175            let cur_op_span = self.token.span;176            let restrictions = if op.node.is_assign_like() {177                self.restrictions & Restrictions::NO_STRUCT_LITERAL178            } else {179                self.restrictions180            };181            let prec = op.node.precedence();182            if match min_prec {183                Bound::Included(min_prec) => prec < min_prec,184                Bound::Excluded(min_prec) => prec <= min_prec,185                Bound::Unbounded => false,186            } {187                break;188            }189            // Check for deprecated `...` syntax190            if self.token == token::DotDotDot && op.node == AssocOp::Range(RangeLimits::Closed) {191                self.err_dotdotdot_syntax(self.token.span);192            }193194            if self.token == token::LArrow {195                self.err_larrow_operator(self.token.span);196            }197198            parsed_something = true;199            self.bump();200            if op.node.is_comparison() {201                if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {202                    return Ok((expr, parsed_something));203                }204            }205206            // Look for JS' `===` and `!==` and recover207            if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node208                && self.token == token::Eq209                && self.prev_token.span.hi() == self.token.span.lo()210            {211                let sp = op.span.to(self.token.span);212                let sugg = bop.as_str().into();213                let invalid = format!("{sugg}=");214                self.dcx().emit_err(diagnostics::InvalidComparisonOperator {215                    span: sp,216                    invalid: invalid.clone(),217                    sub: diagnostics::InvalidComparisonOperatorSub::Correctable {218                        span: sp,219                        invalid,220                        correct: sugg,221                    },222                });223                self.bump();224            }225226            // Look for PHP's `<>` and recover227            if op.node == AssocOp::Binary(BinOpKind::Lt)228                && self.token == token::Gt229                && self.prev_token.span.hi() == self.token.span.lo()230            {231                let sp = op.span.to(self.token.span);232                self.dcx().emit_err(diagnostics::InvalidComparisonOperator {233                    span: sp,234                    invalid: "<>".into(),235                    sub: diagnostics::InvalidComparisonOperatorSub::Correctable {236                        span: sp,237                        invalid: "<>".into(),238                        correct: "!=".into(),239                    },240                });241                self.bump();242            }243244            // Look for C++'s `<=>` and recover245            if op.node == AssocOp::Binary(BinOpKind::Le)246                && self.token == token::Gt247                && self.prev_token.span.hi() == self.token.span.lo()248            {249                let sp = op.span.to(self.token.span);250                self.dcx().emit_err(diagnostics::InvalidComparisonOperator {251                    span: sp,252                    invalid: "<=>".into(),253                    sub: diagnostics::InvalidComparisonOperatorSub::Spaceship(sp),254                });255                self.bump();256            }257258            if self.prev_token == token::Plus259                && self.token == token::Plus260                && self.prev_token.span.between(self.token.span).is_empty()261            {262                let op_span = self.prev_token.span.to(self.token.span);263                // Eat the second `+`264                self.bump();265                lhs = self.recover_from_postfix_increment(lhs, op_span, starts_stmt)?;266                continue;267            }268269            if self.prev_token == token::Minus270                && self.token == token::Minus271                && self.prev_token.span.between(self.token.span).is_empty()272                && !self.look_ahead(1, |tok| tok.can_begin_expr())273            {274                let op_span = self.prev_token.span.to(self.token.span);275                // Eat the second `-`276                self.bump();277                lhs = self.recover_from_postfix_decrement(lhs, op_span, starts_stmt)?;278                continue;279            }280281            let op_span = op.span;282            let op = op.node;283            // Special cases:284            if op == AssocOp::Cast {285                lhs = self.parse_assoc_op_cast(lhs, lhs_span, op_span, ExprKind::Cast)?;286                continue;287            } else if let AssocOp::Range(limits) = op {288                // If we didn't have to handle `x..`/`x..=`, it would be pretty easy to289                // generalise it to the Fixity::None code.290                lhs = self.parse_expr_range(prec, lhs, limits, cur_op_span)?;291                break;292            }293294            let min_prec = match op.fixity() {295                Fixity::Right => Bound::Included(prec),296                Fixity::Left | Fixity::None => Bound::Excluded(prec),297            };298            let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {299                this.parse_expr_assoc(min_prec)300            })?;301302            let span = self.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span);303            lhs = match op {304                AssocOp::Binary(ast_op) => {305                    let binary = self.mk_binary(respan(cur_op_span, ast_op), lhs, rhs);306                    self.mk_expr(span, binary)307                }308                AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)),309                AssocOp::AssignOp(aop) => {310                    let aopexpr = self.mk_assign_op(respan(cur_op_span, aop), lhs, rhs);311                    self.mk_expr(span, aopexpr)312                }313                AssocOp::Cast | AssocOp::Range(_) => {314                    self.dcx().span_bug(span, "AssocOp should have been handled by special case")315                }316            };317        }318319        Ok((lhs, parsed_something))320    }321322    fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {323        match (self.expr_is_complete(lhs), AssocOp::from_token(&self.token)) {324            // Semi-statement forms are odd:325            // See https://github.com/rust-lang/rust/issues/29071326            (true, None) => false,327            (false, _) => true, // Continue parsing the expression.328            // An exhaustive check is done in the following block, but these are checked first329            // because they *are* ambiguous but also reasonable looking incorrect syntax, so we330            // want to keep their span info to improve diagnostics in these cases in a later stage.331            (true, Some(AssocOp::Binary(332                BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3`333                BinOpKind::Sub | // `{ 42 } -5`334                BinOpKind::Add | // `{ 42 } + 42` (unary plus)335                BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`336                BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure)337                BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42`338            ))) => {339                // These cases are ambiguous and can't be identified in the parser alone.340                //341                // Bitwise AND is left out because guessing intent is hard. We can make342                // suggestions based on the assumption that double-refs are rarely intentional,343                // and closures are distinct enough that they don't get mixed up with their344                // return value.345                let sp = self.psess.source_map().start_point(self.token.span);346                self.psess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);347                false348            }349            (true, Some(op)) if !op.can_continue_expr_unambiguously() => false,350            (true, Some(_)) => {351                self.error_found_expr_would_be_stmt(lhs);352                true353            }354        }355    }356357    /// We've found an expression that would be parsed as a statement,358    /// but the next token implies this should be parsed as an expression.359    /// For example: `if let Some(x) = x { x } else { 0 } / 2`.360    fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {361        self.dcx().emit_err(diagnostics::FoundExprWouldBeStmt {362            span: self.token.span,363            token: pprust::token_to_string(&self.token),364            suggestion: ExprParenthesesNeeded::surrounding(lhs.span),365        });366    }367368    /// Possibly translate the current token to an associative operator.369    /// The method does not advance the current token.370    ///371    /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.372    pub(super) fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {373        let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) {374            // When parsing const expressions, stop parsing when encountering `>`.375            (376                Some(377                    AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)378                    | AssocOp::AssignOp(AssignOpKind::ShrAssign),379                ),380                _,381            ) if self.restrictions.contains(Restrictions::CONST_EXPR) => {382                return None;383            }384            // When recovering patterns as expressions, stop parsing when encountering an385            // assignment `=`, an alternative `|`, or a range `..`.386            (387                Some(388                    AssocOp::Assign389                    | AssocOp::AssignOp(_)390                    | AssocOp::Binary(BinOpKind::BitOr)391                    | AssocOp::Range(_),392                ),393                _,394            ) if self.restrictions.contains(Restrictions::IS_PAT) => {395                return None;396            }397            (Some(op), _) => (op, self.token.span),398            (None, Some((Ident { name: sym::and, span }, IdentIsRaw::No)))399                if self.may_recover() =>400            {401                self.dcx().emit_err(diagnostics::InvalidLogicalOperator {402                    span: self.token.span,403                    incorrect: "and".into(),404                    sub: diagnostics::InvalidLogicalOperatorSub::Conjunction(self.token.span),405                });406                (AssocOp::Binary(BinOpKind::And), span)407            }408            (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => {409                self.dcx().emit_err(diagnostics::InvalidLogicalOperator {410                    span: self.token.span,411                    incorrect: "or".into(),412                    sub: diagnostics::InvalidLogicalOperatorSub::Disjunction(self.token.span),413                });414                (AssocOp::Binary(BinOpKind::Or), span)415            }416            _ => return None,417        };418        Some(respan(span, op))419    }420421    /// Checks if this expression is a successfully parsed statement.422    fn expr_is_complete(&self, e: &Expr) -> bool {423        self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)424    }425426    /// Parses `x..y`, `x..=y`, and `x..`/`x..=`.427    /// The other two variants are handled in `parse_prefix_range_expr` below.428    fn parse_expr_range(429        &mut self,430        prec: ExprPrecedence,431        lhs: Box<Expr>,432        limits: RangeLimits,433        cur_op_span: Span,434    ) -> PResult<'a, Box<Expr>> {435        let rhs = if self.is_at_start_of_range_notation_rhs() {436            let maybe_lt = self.token;437            Some(438                self.parse_expr_assoc(Bound::Excluded(prec))439                    .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,440            )441        } else {442            None443        };444        let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);445        let span = self.mk_expr_sp(&lhs, lhs.span, cur_op_span, rhs_span);446        let range = self.mk_range(Some(lhs), rhs, limits);447        Ok(self.mk_expr(span, range))448    }449450    fn is_at_start_of_range_notation_rhs(&self) -> bool {451        if self.token.can_begin_expr() {452            // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.453            if self.token == token::OpenBrace {454                return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);455            }456            true457        } else {458            false459        }460    }461462    /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`.463    fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {464        if !attrs.is_empty() {465            let err = diagnostics::DotDotRangeAttribute { span: self.token.span };466            self.dcx().emit_err(err);467        }468469        // Check for deprecated `...` syntax.470        if self.token == token::DotDotDot {471            self.err_dotdotdot_syntax(self.token.span);472        }473474        debug_assert!(475            self.token.is_range_separator(),476            "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq",477            self.token478        );479480        let limits = match self.token.kind {481            token::DotDot => RangeLimits::HalfOpen,482            _ => RangeLimits::Closed,483        };484        let op = AssocOp::from_token(&self.token);485        self.collect_tokens_for_expr(AttrWrapper::empty(), |this, _empty_attrs| {486            let lo = this.token.span;487            let maybe_lt = this.look_ahead(1, |t| t.clone());488            this.bump();489            let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {490                // RHS must be parsed with more associativity than the dots.491                this.parse_expr_assoc(Bound::Excluded(op.unwrap().precedence()))492                    .map(|expr| (lo.to(expr.span), Some(expr)))493                    .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?494            } else {495                (lo, None)496            };497            let range = this.mk_range(None, opt_end, limits);498            Ok(this.mk_expr(span, range))499        })500    }501502    /// Parses a prefix-unary-operator expr.503    fn parse_expr_prefix(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {504        let lo = self.token.span;505506        macro_rules! make_it {507            ($this:ident, $attrs:expr, |this, _| $body:expr) => {508                $this.collect_tokens_for_expr($attrs, |$this, attrs| {509                    let (hi, ex) = $body?;510                    Ok($this.mk_expr_with_attrs(lo.to(hi), ex, attrs))511                })512            };513        }514515        let this = self;516517        // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()518        match this.token.uninterpolate().kind {519            // `!expr`520            token::Bang => make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Not)),521            // `~expr`522            token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)),523            // `-expr`524            token::Minus => {525                make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Neg))526            }527            // `*expr`528            token::Star => {529                make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Deref))530            }531            // `&expr` and `&&expr`532            token::And | token::AndAnd => {533                make_it!(this, attrs, |this, _| this.parse_expr_borrow(lo))534            }535            // `+lit`536            token::Plus if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {537                let mut err = diagnostics::LeadingPlusNotSupported {538                    span: lo,539                    remove_plus: None,540                    add_parentheses: None,541                };542543                // a block on the LHS might have been intended to be an expression instead544                if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {545                    err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp));546                } else {547                    err.remove_plus = Some(lo);548                }549                this.dcx().emit_err(err);550551                this.bump(); // `+`552                Ok(this.parse_expr_prefix_common(lo)?.1)553            }554            // Recover from `++x`:555            token::Plus if this.look_ahead(1, |t| *t == token::Plus) => {556                let starts_stmt =557                    this.prev_token == token::Semi || this.prev_token == token::CloseBrace;558                let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span));559                // Eat both `+`s.560                this.bump();561                this.bump();562563                let operand_expr = this.parse_expr_dot_or_call(attrs)?;564                this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt)565            }566            token::Ident(..) if this.token.is_keyword(kw::Box) => {567                make_it!(this, attrs, |this, _| this.parse_expr_box(lo))568            }569            token::Ident(..)570                if this.token.is_keyword(kw::Move)571                    && this.look_ahead(1, |t| *t == token::OpenParen) =>572            {573                make_it!(this, attrs, |this, _| this.parse_expr_move(lo))574            }575            token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {576                make_it!(this, attrs, |this, _| this.recover_not_expr(lo))577            }578            _ => return this.parse_expr_dot_or_call(attrs),579        }580    }581582    fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, Box<Expr>)> {583        let attrs = self.parse_outer_attributes()?;584        let expr = if self.token.is_range_separator() {585            self.parse_expr_prefix_range(attrs)586        } else {587            self.parse_expr_prefix(attrs)588        }?;589        let span = self.interpolated_or_expr_span(&expr);590        Ok((lo.to(span), expr))591    }592593    fn parse_expr_unary(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> {594        self.bump(); // `op`595        let (span, expr) = self.parse_expr_prefix_common(lo)?;596        Ok((span, self.mk_unary(op, expr)))597    }598599    /// Recover on `~expr` in favor of `!expr`.600    fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {601        self.dcx().emit_err(diagnostics::TildeAsUnaryOperator(lo));602603        self.parse_expr_unary(lo, UnOp::Not)604    }605606    /// Parse `box expr` - this syntax has been removed, but we still parse this607    /// for now to provide a more useful error608    fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {609        self.bump(); // `box`610        let (span, expr) = self.parse_expr_prefix_common(box_kw)?;611        // Make a multipart suggestion instead of `span_to_snippet` in case source isn't available612        let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));613        let hi = span.shrink_to_hi();614        let sugg = diagnostics::AddBoxNew { box_kw_and_lo, hi };615        let guar = self.dcx().emit_err(diagnostics::BoxSyntaxRemoved { span, sugg });616        Ok((span, ExprKind::Err(guar)))617    }618619    fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> {620        self.bump();621        self.psess.gated_spans.gate(sym::move_expr, move_kw);622        self.expect(exp!(OpenParen))?;623        let expr = self.parse_expr()?;624        self.expect(exp!(CloseParen))?;625        let span = move_kw.to(self.prev_token.span);626        Ok((span, ExprKind::Move(expr, move_kw)))627    }628629    fn is_mistaken_not_ident_negation(&self) -> bool {630        let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {631            // These tokens can start an expression after `!`, but632            // can't continue an expression after an ident633            token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),634            token::Literal(..) | token::Pound => true,635            _ => t.is_metavar_expr(),636        };637        self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr)638    }639640    /// Recover on `not expr` in favor of `!expr`.641    fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {642        let negated_token = self.look_ahead(1, |t| *t);643644        let sub_diag = if negated_token.is_numeric_lit() {645            diagnostics::NotAsNegationOperatorSub::SuggestNotBitwise646        } else if negated_token.is_bool_lit() {647            diagnostics::NotAsNegationOperatorSub::SuggestNotLogical648        } else {649            diagnostics::NotAsNegationOperatorSub::SuggestNotDefault650        };651652        self.dcx().emit_err(diagnostics::NotAsNegationOperator {653            negated: negated_token.span,654            negated_desc: super::token_descr(&negated_token),655            // Span the `not` plus trailing whitespace to avoid656            // trailing whitespace after the `!` in our suggestion657            sub: sub_diag(658                self.psess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),659            ),660        });661662        self.parse_expr_unary(lo, UnOp::Not)663    }664665    /// Returns the span of expr if it was not interpolated, or the span of the interpolated token.666    fn interpolated_or_expr_span(&self, expr: &Expr) -> Span {667        match self.prev_token.kind {668            token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span,669            token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => {670                // `expr.span` is the interpolated span, because invisible open671                // and close delims both get marked with the same span, one672                // that covers the entire thing between them. (See673                // `rustc_expand::mbe::transcribe::transcribe`.)674                self.prev_token.span675            }676            _ => expr.span,677        }678    }679680    fn parse_assoc_op_cast(681        &mut self,682        lhs: Box<Expr>,683        lhs_span: Span,684        op_span: Span,685        expr_kind: fn(Box<Expr>, Box<Ty>) -> ExprKind,686    ) -> PResult<'a, Box<Expr>> {687        let mk_expr = |this: &mut Self, lhs: Box<Expr>, rhs: Box<Ty>| {688            this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs))689        };690691        // Save the state of the parser before parsing type normally, in case there is a692        // LessThan comparison after this cast.693        let parser_snapshot_before_type = self.clone();694        let cast_expr = match self.parse_as_cast_ty() {695            Ok(rhs) => mk_expr(self, lhs, rhs),696            Err(type_err) => {697                if !self.may_recover() {698                    return Err(type_err);699                }700701                // Rewind to before attempting to parse the type with generics, to recover702                // from situations like `x as usize < y` in which we first tried to parse703                // `usize < y` as a type with generic arguments.704                let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type);705706                // Check for typo of `'a: loop { break 'a }` with a missing `'`.707                match (&lhs.kind, &self.token.kind) {708                    (709                        // `foo: `710                        ExprKind::Path(None, ast::Path { segments, .. }),711                        token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No),712                    ) if let [segment] = segments.as_slice() => {713                        let snapshot = self.create_snapshot_for_diagnostic();714                        let label = Label {715                            ident: Ident::from_str_and_span(716                                &format!("'{}", segment.ident),717                                segment.ident.span,718                            ),719                        };720                        match self.parse_expr_labeled(label, false) {721                            Ok(expr) => {722                                type_err.cancel();723                                self.dcx().emit_err(diagnostics::MalformedLoopLabel {724                                    span: label.ident.span,725                                    suggestion: label.ident.span.shrink_to_lo(),726                                });727                                return Ok(expr);728                            }729                            Err(err) => {730                                err.cancel();731                                self.restore_snapshot(snapshot);732                            }733                        }734                    }735                    _ => {}736                }737738                match self.parse_path(PathStyle::Expr) {739                    Ok(path) => {740                        let span_after_type = parser_snapshot_after_type.token.span;741                        let expr = mk_expr(742                            self,743                            lhs,744                            self.mk_ty(path.span, TyKind::Path(None, path.clone())),745                        );746747                        let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);748                        match self.token.kind {749                            token::Lt => {750                                self.dcx().emit_err(diagnostics::ComparisonInterpretedAsGeneric {751                                    comparison: self.token.span,752                                    r#type: pprust::path_to_string(&path),753                                    args: args_span,754                                    suggestion: diagnostics::ComparisonInterpretedAsGenericSugg {755                                        left: expr.span.shrink_to_lo(),756                                        right: expr.span.shrink_to_hi(),757                                    },758                                })759                            }760                            token::Shl => {761                                self.dcx().emit_err(diagnostics::ShiftInterpretedAsGeneric {762                                    shift: self.token.span,763                                    r#type: pprust::path_to_string(&path),764                                    args: args_span,765                                    suggestion: diagnostics::ShiftInterpretedAsGenericSugg {766                                        left: expr.span.shrink_to_lo(),767                                        right: expr.span.shrink_to_hi(),768                                    },769                                })770                            }771                            _ => {772                                // We can end up here even without `<` being the next token, for773                                // example because `parse_ty_no_plus` returns `Err` on keywords,774                                // but `parse_path` returns `Ok` on them due to error recovery.775                                // Return original error and parser state.776                                *self = parser_snapshot_after_type;777                                return Err(type_err);778                            }779                        };780781                        // Successfully parsed the type path leaving a `<` yet to parse.782                        type_err.cancel();783784                        // Keep `x as usize` as an expression in AST and continue parsing.785                        expr786                    }787                    Err(path_err) => {788                        // Couldn't parse as a path, return original error and parser state.789                        path_err.cancel();790                        *self = parser_snapshot_after_type;791                        return Err(type_err);792                    }793                }794            }795        };796797        // Try to parse a postfix operator such as `.`, `?`, or index (`[]`)798        // after a cast. If one is present, emit an error then return a valid799        // parse tree; For something like `&x as T[0]` will be as if it was800        // written `((&x) as T)[0]`.801802        let span = cast_expr.span;803804        let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?;805806        // Check if an illegal postfix operator has been added after the cast.807        // If the resulting expression is not a cast, it is an illegal postfix operator.808        if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {809            let msg = format!(810                "cast cannot be followed by {}",811                match with_postfix.kind {812                    ExprKind::Index(..) => "indexing",813                    ExprKind::Try(_) => "`?`",814                    ExprKind::Field(_, _) => "a field access",815                    ExprKind::MethodCall(_) => "a method call",816                    ExprKind::Call(_, _) => "a function call",817                    ExprKind::Await(_, _) => "`.await`",818                    ExprKind::Use(_, _) => "`.use`",819                    ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`",820                    ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",821                    ExprKind::Err(_) => return Ok(with_postfix),822                    _ => unreachable!(823                        "did not expect {:?} as an illegal postfix operator following cast",824                        with_postfix.kind825                    ),826                }827            );828            let mut err = self.dcx().struct_span_err(span, msg);829830            let suggest_parens = |err: &mut Diag<'_>| {831                let suggestions = vec![832                    (span.shrink_to_lo(), "(".to_string()),833                    (span.shrink_to_hi(), ")".to_string()),834                ];835                err.multipart_suggestion(836                    "try surrounding the expression in parentheses",837                    suggestions,838                    Applicability::MachineApplicable,839                );840            };841842            suggest_parens(&mut err);843844            err.emit();845        };846        Ok(with_postfix)847    }848849    /// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.850    fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {851        self.expect_and()?;852        let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);853        let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.854        let (borrow_kind, mutbl) = self.parse_borrow_modifiers();855        let (span, expr) = self.parse_expr_prefix_common(lo)?;856        if let Some(lt) = lifetime {857            self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));858        }859860        // Add expected tokens if we parsed `&raw` as an expression.861        // This will make sure we see "expected `const`, `mut`", and862        // guides recovery in case we write `&raw expr`.863        if borrow_kind == ast::BorrowKind::Ref864            && mutbl == ast::Mutability::Not865            && matches!(&expr.kind, ExprKind::Path(None, p) if *p == kw::Raw)866        {867            self.expected_token_types.insert(TokenType::KwMut);868            self.expected_token_types.insert(TokenType::KwConst);869        }870871        Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))872    }873874    fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {875        self.dcx()876            .emit_err(diagnostics::LifetimeInBorrowExpression { span, lifetime_span: lt_span });877    }878879    /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.880    fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {881        if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {882            // `raw [ const | mut ]`.883            let found_raw = self.eat_keyword(exp!(Raw));884            assert!(found_raw);885            let mutability = self.parse_mut_or_const().unwrap();886            (ast::BorrowKind::Raw, mutability)887        } else {888            match self.parse_pin_and_mut() {889                // `mut?`890                (ast::Pinnedness::Not, mutbl) => (ast::BorrowKind::Ref, mutbl),891                // `pin [ const | mut ]`.892                // `pin` has been gated in `self.parse_pin_and_mut()` so we don't893                // need to gate it here.894                (ast::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl),895            }896        }897    }898899    /// Parses `a.b` or `a(13)` or `a[4]` or just `a`.900    fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {901        self.collect_tokens_for_expr(attrs, |this, attrs| {902            let base = this.parse_expr_bottom()?;903            let span = this.interpolated_or_expr_span(&base);904            this.parse_expr_dot_or_call_with(attrs, base, span)905        })906    }907908    pub(super) fn parse_expr_dot_or_call_with(909        &mut self,910        mut attrs: ast::AttrVec,911        mut e: Box<Expr>,912        lo: Span,913    ) -> PResult<'a, Box<Expr>> {914        let mut res = loop {915            let has_question = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {916                // We are using noexpect here because we don't expect a `?` directly after917                // a `return` which could be suggested otherwise.918                self.eat_noexpect(&token::Question)919            } else {920                self.eat(exp!(Question))921            };922            if has_question {923                // `expr?`924                e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e));925                continue;926            }927            let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {928                // We are using noexpect here because we don't expect a `.` directly after929                // a `return` which could be suggested otherwise.930                self.eat_noexpect(&token::Dot)931            } else if self.token == TokenKind::RArrow && self.may_recover() {932                // Recovery for `expr->suffix`.933                self.bump();934                let span = self.prev_token.span;935                self.dcx().emit_err(diagnostics::ExprRArrowCall { span });936                true937            } else {938                self.eat(exp!(Dot))939            };940            if has_dot {941                // expr.f942                e = self.parse_dot_suffix_expr(lo, e)?;943                continue;944            }945            if self.expr_is_complete(&e) {946                break Ok(e);947            }948            e = match self.token.kind {949                token::OpenParen => self.parse_expr_fn_call(lo, e),950                token::OpenBracket => self.parse_expr_index(lo, e)?,951                _ => break Ok(e),952            }953        };954955        // Stitch the list of outer attributes onto the return value. A little956        // bit ugly, but the best way given the current code structure.957        if !attrs.is_empty()958            && let Ok(expr) = &mut res959        {960            mem::swap(&mut expr.attrs, &mut attrs);961            expr.attrs.extend(attrs)962        }963        res964    }965966    pub(super) fn parse_dot_suffix_expr(967        &mut self,968        lo: Span,969        base: Box<Expr>,970    ) -> PResult<'a, Box<Expr>> {971        // At this point we've consumed something like `expr.` and `self.token` holds the token972        // after the dot.973        match self.token.uninterpolate().kind {974            token::Ident(..) => self.parse_dot_suffix(base, lo),975            token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {976                let ident_span = self.token.span;977                self.bump();978                Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix))979            }980            token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => {981                Ok(match self.break_up_float(symbol, self.token.span) {982                    // 1e2983                    DestructuredFloat::Single(sym, _sp) => {984                        // `foo.1e2`: a single complete dot access, fully consumed. We end up with985                        // the `1e2` token in `self.prev_token` and the following token in986                        // `self.token`.987                        let ident_span = self.token.span;988                        self.bump();989                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix)990                    }991                    // 1.992                    DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {993                        // `foo.1.`: a single complete dot access and the start of another.994                        // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in995                        // `self.token`.996                        assert!(suffix.is_none());997                        self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span);998                        self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));999                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None)1000                    }1001                    // 1.2 | 1.2e31002                    DestructuredFloat::MiddleDot(1003                        sym1,1004                        ident1_span,1005                        _dot_span,1006                        sym2,1007                        ident2_span,1008                    ) => {1009                        // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with1010                        // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following1011                        // token in `self.token`.1012                        let next_token2 =1013                            Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span);1014                        self.bump_with((next_token2, self.token_spacing));1015                        self.bump();1016                        let base1 =1017                            self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None);1018                        self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix)1019                    }1020                    DestructuredFloat::Error => base,1021                })1022            }1023            _ => {1024                self.error_unexpected_after_dot();1025                Ok(base)1026            }1027        }1028    }10291030    fn error_unexpected_after_dot(&self) {1031        let actual = super::token_descr(&self.token);1032        let span = self.token.span;1033        let sm = self.psess.source_map();1034        let (span, actual) = match (&self.token.kind, self.subparser_name) {1035            (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => {1036                (span.shrink_to_hi(), format!("`{}`", snippet))1037            }1038            (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => {1039                // No need to report an error. This case will only occur when parsing a pasted1040                // metavariable, and we should have emitted an error when parsing the macro call in1041                // the first place. E.g. in this code:1042                // ```1043                // macro_rules! m { ($e:expr) => { $e }; }1044                //1045                // fn main() {1046                //     let f = 1;1047                //     m!(f.);1048                // }1049                // ```1050                // we'll get an error "unexpected token: `)` when parsing the `m!(f.)`, so we don't1051                // want to issue a second error when parsing the expansion `«f.»` (where `«`/`»`1052                // represent the invisible delimiters).1053                self.dcx().span_delayed_bug(span, "bad dot expr in metavariable");1054                return;1055            }1056            _ => (span, actual),1057        };1058        self.dcx().emit_err(diagnostics::UnexpectedTokenAfterDot { span, actual });1059    }10601061    /// We need an identifier or integer, but the next token is a float.1062    /// Break the float into components to extract the identifier or integer.1063    ///1064    /// See also [`TokenKind::break_two_token_op`] which does similar splitting of `>>` into `>`.1065    //1066    // FIXME: With current `TokenCursor` it's hard to break tokens into more than 21067    //  parts unless those parts are processed immediately. `TokenCursor` should either1068    //  support pushing "future tokens" (would be also helpful to `break_and_eat`), or1069    //  we should break everything including floats into more basic proc-macro style1070    //  tokens in the lexer (probably preferable).1071    pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {1072        #[derive(Debug)]1073        enum FloatComponent {1074            IdentLike(String),1075            Punct(char),1076        }1077        use FloatComponent::*;10781079        let float_str = float.as_str();1080        let mut components = Vec::new();1081        let mut ident_like = String::new();1082        for c in float_str.chars() {1083            if c == '_' || c.is_ascii_alphanumeric() {1084                ident_like.push(c);1085            } else if matches!(c, '.' | '+' | '-') {1086                if !ident_like.is_empty() {1087                    components.push(IdentLike(mem::take(&mut ident_like)));1088                }1089                components.push(Punct(c));1090            } else {1091                panic!("unexpected character in a float token: {c:?}")1092            }1093        }1094        if !ident_like.is_empty() {1095            components.push(IdentLike(ident_like));1096        }10971098        // With proc macros the span can refer to anything, the source may be too short,1099        // or too long, or non-ASCII. It only makes sense to break our span into components1100        // if its underlying text is identical to our float literal.1101        let can_take_span_apart =1102            || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref();11031104        match &*components {1105            // 1e21106            [IdentLike(i)] => DestructuredFloat::Single(Symbol::intern(i), span),1107            // 1.1108            [IdentLike(left), Punct('.')] => {1109                let (left_span, dot_span) = if can_take_span_apart() {1110                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1111                    let dot_span = span.with_lo(left_span.hi());1112                    (left_span, dot_span)1113                } else {1114                    (span, span)1115                };1116                let left = Symbol::intern(left);1117                DestructuredFloat::TrailingDot(left, left_span, dot_span)1118            }1119            // 1.2 | 1.2e31120            [IdentLike(left), Punct('.'), IdentLike(right)] => {1121                let (left_span, dot_span, right_span) = if can_take_span_apart() {1122                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1123                    let dot_span =1124                        span.with_lo(left_span.hi()).with_hi(left_span.hi() + BytePos(1));1125                    let right_span = span.with_lo(dot_span.hi());1126                    (left_span, dot_span, right_span)1127                } else {1128                    (span, span, span)1129                };1130                let left = Symbol::intern(left);1131                let right = Symbol::intern(right);1132                DestructuredFloat::MiddleDot(left, left_span, dot_span, right, right_span)1133            }1134            // 1e+ | 1e- (recovered)1135            [IdentLike(_), Punct('+' | '-')] |1136            // 1e+2 | 1e-21137            [IdentLike(_), Punct('+' | '-'), IdentLike(_)] |1138            // 1.2e+ | 1.2e-1139            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] |1140            // 1.2e+3 | 1.2e-31141            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {1142                // See the FIXME about `TokenCursor` above.1143                self.error_unexpected_after_dot();1144                DestructuredFloat::Error1145            }1146            _ => panic!("unexpected components in a float token: {components:?}"),1147        }1148    }11491150    /// Parse the field access used in offset_of, matched by `$(e:expr)+`.1151    /// Currently returns a list of idents. However, it should be possible in1152    /// future to also do array indices, which might be arbitrary expressions.1153    pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, ThinVec<Ident>> {1154        let mut fields = ThinVec::new();1155        let mut trailing_dot = None;11561157        loop {1158            // This is expected to use a metavariable $(args:expr)+, but the builtin syntax1159            // could be called directly. Calling `parse_expr` allows this function to only1160            // consider `Expr`s.1161            let expr = self.parse_expr()?;1162            let mut current = &expr;1163            let start_idx = fields.len();1164            loop {1165                match current.kind {1166                    ExprKind::Field(ref left, right) => {1167                        // Field access is read right-to-left.1168                        fields.insert(start_idx, right);1169                        trailing_dot = None;1170                        current = left;1171                    }1172                    // Parse this both to give helpful error messages and to1173                    // verify it can be done with this parser setup.1174                    ExprKind::Index(ref left, ref _right, span) => {1175                        self.dcx().emit_err(diagnostics::ArrayIndexInOffsetOf(span));1176                        current = left;1177                    }1178                    ExprKind::Lit(token::Lit {1179                        kind: token::Float | token::Integer,1180                        symbol,1181                        suffix,1182                    }) => {1183                        if let Some(suffix) = suffix {1184                            self.dcx().emit_err(diagnostics::InvalidLiteralSuffixOnTupleIndex {1185                                span: current.span,1186                                suffix,1187                            });1188                        }1189                        match self.break_up_float(symbol, current.span) {1190                            // 1e21191                            DestructuredFloat::Single(sym, sp) => {1192                                trailing_dot = None;1193                                fields.insert(start_idx, Ident::new(sym, sp));1194                            }1195                            // 1.1196                            DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {1197                                assert!(suffix.is_none());1198                                trailing_dot = Some(dot_span);1199                                fields.insert(start_idx, Ident::new(sym, sym_span));1200                            }1201                            // 1.2 | 1.2e31202                            DestructuredFloat::MiddleDot(1203                                symbol1,1204                                span1,1205                                _dot_span,1206                                symbol2,1207                                span2,1208                            ) => {1209                                trailing_dot = None;1210                                fields.insert(start_idx, Ident::new(symbol2, span2));1211                                fields.insert(start_idx, Ident::new(symbol1, span1));1212                            }1213                            DestructuredFloat::Error => {1214                                trailing_dot = None;1215                                fields.insert(start_idx, Ident::new(symbol, self.prev_token.span));1216                            }1217                        }1218                        break;1219                    }1220                    ExprKind::Path(None, Path { ref segments, .. }) => {1221                        match &segments[..] {1222                            [PathSegment { ident, args: None, .. }] => {1223                                trailing_dot = None;1224                                fields.insert(start_idx, *ident)1225                            }1226                            _ => {1227                                self.dcx().emit_err(diagnostics::InvalidOffsetOf(current.span));1228                                break;1229                            }1230                        }1231                        break;1232                    }1233                    _ => {1234                        self.dcx().emit_err(diagnostics::InvalidOffsetOf(current.span));1235                        break;1236                    }1237                }1238            }12391240            if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma {1241                break;1242            } else if trailing_dot.is_none() {1243                // This loop should only repeat if there is a trailing dot.1244                self.dcx().emit_err(diagnostics::InvalidOffsetOf(self.token.span));1245                break;1246            }1247        }1248        if let Some(dot) = trailing_dot {1249            self.dcx().emit_err(diagnostics::InvalidOffsetOf(dot));1250        }1251        Ok(fields.into_iter().collect())1252    }12531254    fn mk_expr_tuple_field_access(1255        &self,1256        lo: Span,1257        ident_span: Span,1258        base: Box<Expr>,1259        field: Symbol,1260        suffix: Option<Symbol>,1261    ) -> Box<Expr> {1262        if let Some(suffix) = suffix {1263            self.dcx().emit_err(diagnostics::InvalidLiteralSuffixOnTupleIndex {1264                span: ident_span,1265                suffix,1266            });1267        }1268        self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span)))1269    }12701271    /// Parse a function call expression, `expr(...)`.1272    fn parse_expr_fn_call(&mut self, lo: Span, fun: Box<Expr>) -> Box<Expr> {1273        let snapshot = if self.token == token::OpenParen {1274            Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))1275        } else {1276            None1277        };1278        let open_paren = self.token.span;1279        let call_depth = self.token_cursor.depth();12801281        let seq = match self.parse_expr_paren_seq() {1282            Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))),1283            Err(err)1284                if self.is_expected_raw_ref_mut() && self.token_cursor.depth() == call_depth =>1285            {1286                let guar = err.emit();1287                // Preserve the call expression so later passes can still diagnose the callee,1288                // while treating the malformed `&raw <expr>` argument as an error expression.1289                let args = self.recover_raw_ref_call_args(guar);1290                return self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args));1291            }1292            Err(err) => Err(err),1293        };1294        match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) {1295            Ok(expr) => expr,1296            Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err),1297        }1298    }12991300    fn recover_raw_ref_call_args(&mut self, guar: ErrorGuaranteed) -> ThinVec<Box<Expr>> {1301        let err_span = self.prev_token.span.to(self.token.span);1302        let mut args = thin_vec![self.mk_expr_err(err_span, guar)];1303        while !self.token.kind.is_close_delim_or_eof() {1304            if self.eat(exp!(Comma)) {1305                if !self.token.kind.is_close_delim_or_eof() {1306                    args.push(self.mk_expr_err(self.prev_token.span.shrink_to_hi(), guar));1307                }1308            } else {1309                self.parse_token_tree();1310            }1311        }1312        let _ = self.eat(exp!(CloseParen));1313        args1314    }13151316    /// If we encounter a parser state that looks like the user has written a `struct` literal with1317    /// parentheses instead of braces, recover the parser state and provide suggestions.1318    #[instrument(skip(self, seq, snapshot), level = "trace")]1319    fn maybe_recover_struct_lit_bad_delims(1320        &mut self,1321        lo: Span,1322        open_paren: Span,1323        seq: PResult<'a, Box<Expr>>,1324        snapshot: Option<(SnapshotParser<'a>, ExprKind)>,1325    ) -> PResult<'a, Box<Expr>> {1326        match (self.may_recover(), seq, snapshot) {1327            (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {1328                snapshot.bump(); // `(`1329                match snapshot.parse_struct_fields(path.clone(), false, exp!(CloseParen)) {1330                    Ok((fields, ..)) if snapshot.eat(exp!(CloseParen)) => {1331                        // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest1332                        // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.1333                        self.restore_snapshot(snapshot);1334                        let close_paren = self.prev_token.span;1335                        let span = lo.to(close_paren);1336                        // filter shorthand fields1337                        let fields: Vec<_> =1338                            fields.into_iter().filter(|field| !field.is_shorthand).collect();13391340                        let guar = if !fields.is_empty() &&1341                            // `token.kind` should not be compared here.1342                            // This is because the `snapshot.token.kind` is treated as the same as1343                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even1344                            // if they are different.1345                            self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")1346                        {1347                            err.cancel();1348                            let type_str = pprust::path_to_string(&path);1349                            self.dcx()1350                                .create_err(diagnostics::ParenthesesWithStructFields {1351                                    span,1352                                    braces_for_struct: diagnostics::BracesForStructLiteral {1353                                        first: open_paren,1354                                        second: close_paren,1355                                        r#type: type_str.clone(),1356                                    },1357                                    no_fields_for_fn: diagnostics::NoFieldsForFnCall {1358                                        r#type: type_str,1359                                        fields: fields1360                                            .into_iter()1361                                            .map(|field| field.span.until(field.expr.span))1362                                            .collect(),1363                                    },1364                                })1365                                .emit()1366                        } else {1367                            err.emit()1368                        };1369                        Ok(self.mk_expr_err(span, guar))1370                    }1371                    Ok(_) => Err(err),1372                    Err(err2) => {1373                        err2.cancel();1374                        Err(err)1375                    }1376                }1377            }1378            (_, seq, _) => seq,1379        }1380    }13811382    /// Parse an indexing expression `expr[...]`.1383    fn parse_expr_index(&mut self, lo: Span, base: Box<Expr>) -> PResult<'a, Box<Expr>> {1384        let prev_token = self.prev_token;1385        let open_delim_span = self.token.span;1386        self.bump(); // `[`1387        let index = self.parse_expr()?;1388        self.suggest_missing_semicolon_before_array(prev_token.span, open_delim_span)?;1389        self.expect(exp!(CloseBracket)).map_err(|mut e| {1390            if let TokenKind::Ident(_, _) = prev_token.kind {1391                e.span_suggestion_verbose(1392                    prev_token.span.shrink_to_hi(),1393                    "you might have meant to call a macro",1394                    "!".to_string(),1395                    Applicability::MaybeIncorrect,1396                );1397            }1398            e1399        })?;1400        Ok(self.mk_expr(1401            lo.to(self.prev_token.span),1402            self.mk_index(base, index, open_delim_span.to(self.prev_token.span)),1403        ))1404    }14051406    /// Assuming we have just parsed `.`, continue parsing into an expression.1407    fn parse_dot_suffix(&mut self, self_arg: Box<Expr>, lo: Span) -> PResult<'a, Box<Expr>> {1408        if self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) {1409            return Ok(self.mk_await_expr(self_arg, lo));1410        }14111412        if self.eat_keyword(exp!(Use)) {1413            let use_span = self.prev_token.span;1414            self.psess.gated_spans.gate(sym::ergonomic_clones, use_span);1415            return Ok(self.mk_use_expr(self_arg, lo));1416        }14171418        // Post-fix match1419        if self.eat_keyword(exp!(Match)) {1420            let match_span = self.prev_token.span;1421            self.psess.gated_spans.gate(sym::postfix_match, match_span);1422            return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);1423        }14241425        // Parse a postfix `yield`.1426        if self.eat_keyword(exp!(Yield)) {1427            let yield_span = self.prev_token.span;1428            self.psess.gated_spans.gate(sym::yield_expr, yield_span);1429            return Ok(1430                self.mk_expr(lo.to(yield_span), ExprKind::Yield(YieldKind::Postfix(self_arg)))1431            );1432        }14331434        let fn_span_lo = self.token.span;1435        let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;1436        self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]);1437        self.check_turbofish_missing_angle_brackets(&mut seg);14381439        if self.check(exp!(OpenParen)) {1440            // Method call `expr.f()`1441            let args = self.parse_expr_paren_seq()?;1442            let fn_span = fn_span_lo.to(self.prev_token.span);1443            let span = lo.to(self.prev_token.span);1444            Ok(self.mk_expr(1445                span,1446                ExprKind::MethodCall(Box::new(ast::MethodCall {1447                    seg,1448                    receiver: self_arg,1449                    args,1450                    span: fn_span,1451                })),1452            ))1453        } else {1454            // Field access `expr.f`1455            let span = lo.to(self.prev_token.span);1456            if let Some(args) = seg.args {1457                // See `StashKey::GenericInFieldExpr` for more info on why we stash this.1458                self.dcx()1459                    .create_err(diagnostics::FieldExpressionWithGeneric(args.span()))1460                    .stash(seg.ident.span, StashKey::GenericInFieldExpr);1461            }14621463            Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident)))1464        }1465    }14661467    /// At the bottom (top?) of the precedence hierarchy,1468    /// Parses things like parenthesized exprs, macros, `return`, etc.1469    ///1470    /// N.B., this does not parse outer attributes, and is private because it only works1471    /// correctly if called from `parse_expr_dot_or_call`.1472    fn parse_expr_bottom(&mut self) -> PResult<'a, Box<Expr>> {1473        maybe_recover_from_interpolated_ty_qpath!(self, true);14741475        let span = self.token.span;1476        if let Some(expr) = self.eat_metavar_seq_with_matcher(1477            |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),1478            |this| {1479                // Force collection (as opposed to just `parse_expr`) is required to avoid the1480                // attribute duplication seen in #138478.1481                let expr = this.parse_expr_force_collect();1482                // FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly1483                // related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line1484                // `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in1485                // `compiler/rustc_index/src/bit_set/tests.rs`.1486                if this.token.kind == token::Comma {1487                    this.bump();1488                }1489                expr1490            },1491        ) {1492            return Ok(expr);1493        } else if let Some(lit) =1494            self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())1495        {1496            return Ok(lit);1497        } else if let Some(block) =1498            self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block())1499        {1500            return Ok(self.mk_expr(span, ExprKind::Block(block, None)));1501        } else if let Some(path) =1502            self.eat_metavar_seq(MetaVarKind::Path, |this| this.parse_path(PathStyle::Type))1503        {1504            return Ok(self.mk_expr(span, ExprKind::Path(None, path)));1505        }15061507        // Outer attributes are already parsed and will be1508        // added to the return value after the fact.15091510        let restrictions = self.restrictions;1511        self.with_res(restrictions - Restrictions::ALLOW_LET, |this| {1512            // Note: adding new syntax here? Don't forget to adjust `TokenKind::can_begin_expr()`.1513            let lo = this.token.span;1514            if let token::Literal(_) = this.token.kind {1515                // This match arm is a special-case of the `_` match arm below and1516                // could be removed without changing functionality, but it's faster1517                // to have it here, especially for programs with large constants.1518                this.parse_expr_lit()1519            } else if this.check(exp!(OpenParen)) {1520                this.parse_expr_tuple_parens(restrictions)1521            } else if this.check(exp!(OpenBrace)) {1522                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {1523                    return Ok(expr);1524                }1525                this.parse_expr_block(None, lo, BlockCheckMode::Default)1526            } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {1527                this.parse_expr_closure().map_err(|mut err| {1528                    // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`1529                    // then suggest parens around the lhs.1530                    if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {1531                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));1532                    }1533                    err1534                })1535            } else if this.check(exp!(OpenBracket)) {1536                this.parse_expr_array_or_repeat(exp!(CloseBracket))1537            } else if this.is_builtin() {1538                this.parse_expr_builtin()1539            } else if this.check_path() {1540                this.parse_expr_path_start()1541            } else if this.check_keyword(exp!(Move))1542                || this.check_keyword(exp!(Use))1543                || this.check_keyword(exp!(Static))1544                || this.check_const_closure()1545            {1546                this.parse_expr_closure()1547            } else if this.eat_keyword(exp!(If)) {1548                this.parse_expr_if()1549            } else if this.check_keyword(exp!(For)) {1550                if this.choose_generics_over_qpath(1) {1551                    this.parse_expr_closure()1552                } else {1553                    assert!(this.eat_keyword(exp!(For)));1554                    this.parse_expr_for(None, lo)1555                }1556            } else if this.eat_keyword(exp!(While)) {1557                this.parse_expr_while(None, lo)1558            } else if let Some(label) = this.eat_label() {1559                this.parse_expr_labeled(label, true)1560            } else if this.eat_keyword(exp!(Loop)) {1561                this.parse_expr_loop(None, lo).map_err(|mut err| {1562                    err.span_label(lo, "while parsing this `loop` expression");1563                    err1564                })1565            } else if this.eat_keyword(exp!(Match)) {1566                this.parse_expr_match().map_err(|mut err| {1567                    err.span_label(lo, "while parsing this `match` expression");1568                    err1569                })1570            } else if this.eat_keyword(exp!(Unsafe)) {1571                this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(1572                    |mut err| {1573                        err.span_label(lo, "while parsing this `unsafe` expression");1574                        err1575                    },1576                )1577            } else if this.check_inline_const(0) {1578                this.parse_const_block(lo, false)1579            } else if this.may_recover() && this.is_do_catch_block() {1580                this.recover_do_catch()1581            } else if this.is_try_block() {1582                this.expect_keyword(exp!(Try))?;1583                this.parse_try_block(lo)1584            } else if this.eat_keyword(exp!(Return)) {1585                this.parse_expr_return()1586            } else if this.eat_keyword(exp!(Continue)) {1587                this.parse_expr_continue(lo)1588            } else if this.eat_keyword(exp!(Break)) {1589                this.parse_expr_break()1590            } else if this.eat_keyword(exp!(Yield)) {1591                this.parse_expr_yield()1592            } else if this.is_do_yeet() {1593                this.parse_expr_yeet()1594            } else if this.eat_keyword(exp!(Become)) {1595                this.parse_expr_become()1596            } else if this.check_keyword(exp!(Let)) {1597                this.parse_expr_let(restrictions)1598            } else if this.eat_keyword(exp!(Underscore)) {1599                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(true)? {1600                    return Ok(expr);1601                }1602                Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))1603            } else if this.token_uninterpolated_span().at_least_rust_2018() {1604                // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.1605                let at_async = this.check_keyword(exp!(Async));1606                // check for `gen {}` and `gen move {}`1607                // or `async gen {}` and `async gen move {}`1608                // FIXME: (async) gen closures aren't yet parsed.1609                // FIXME(gen_blocks): Parse `gen async` and suggest swap1610                if this.token_uninterpolated_span().at_least_rust_2024()1611                    && this.is_gen_block(kw::Gen, at_async as usize)1612                {1613                    this.parse_gen_block()1614                // Check for `async {` and `async move {`,1615                } else if this.is_gen_block(kw::Async, 0) {1616                    this.parse_gen_block()1617                } else if at_async {1618                    this.parse_expr_closure()1619                } else if this.eat_keyword_noexpect(kw::Await) {1620                    this.recover_incorrect_await_syntax(lo)1621                } else {1622                    this.parse_expr_lit()1623                }1624            } else {1625                this.parse_expr_lit()1626            }1627        })1628    }16291630    fn parse_expr_lit(&mut self) -> PResult<'a, Box<Expr>> {1631        let lo = self.token.span;1632        match self.parse_opt_token_lit() {1633            Some((token_lit, _)) => {1634                let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit));1635                self.maybe_recover_from_bad_qpath(expr)1636            }1637            None => self.try_macro_suggestion(),1638        }1639    }16401641    fn parse_expr_tuple_parens(&mut self, restrictions: Restrictions) -> PResult<'a, Box<Expr>> {1642        let lo = self.token.span;1643        self.expect(exp!(OpenParen))?;1644        let (es, trailing_comma) = match self.parse_seq_to_end(1645            exp!(CloseParen),1646            SeqSep::trailing_allowed(exp!(Comma)),1647            |p| p.parse_expr_catch_underscore(restrictions.intersection(Restrictions::ALLOW_LET)),1648        ) {1649            Ok(x) => x,1650            Err(err) => {1651                return Ok(self.recover_seq_parse_error(1652                    exp!(OpenParen),1653                    exp!(CloseParen),1654                    lo,1655                    err,1656                ));1657            }1658        };1659        let kind = if es.len() == 1 && matches!(trailing_comma, Trailing::No) {1660            // `(e)` is parenthesized `e`.1661            ExprKind::Paren(es.into_iter().next().unwrap())1662        } else {1663            // `(e,)` is a tuple with only one field, `e`.1664            ExprKind::Tup(es)1665        };1666        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1667        self.maybe_recover_from_bad_qpath(expr)1668    }16691670    fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair) -> PResult<'a, Box<Expr>> {1671        let lo = self.token.span;1672        self.bump(); // `[` or other open delim16731674        let kind = if self.eat(close) {1675            // Empty vector1676            ExprKind::Array(ThinVec::new())1677        } else {1678            // Non-empty vector1679            let first_expr = self.parse_expr()?;1680            if self.eat(exp!(Semi)) {1681                // Repeating array syntax: `[ 0; 512 ]`1682                let count = self.parse_expr_anon_const()?;1683                self.expect(close)?;1684                ExprKind::Repeat(first_expr, count)1685            } else if self.eat(exp!(Comma)) {1686                // Vector with two or more elements.1687                let sep = SeqSep::trailing_allowed(exp!(Comma));1688                let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;1689                exprs.insert(0, first_expr);1690                ExprKind::Array(exprs)1691            } else {1692                // Vector with one element1693                self.expect(close)?;1694                ExprKind::Array(thin_vec![first_expr])1695            }1696        };1697        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1698        self.maybe_recover_from_bad_qpath(expr)1699    }17001701    fn parse_expr_path_start(&mut self) -> PResult<'a, Box<Expr>> {1702        let maybe_eq_tok = self.prev_token;1703        let (qself, path) = if self.eat_lt() {1704            let lt_span = self.prev_token.span;1705            let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {1706                // Suggests using '<=' if there is an error parsing qpath when the previous token1707                // is an '=' token. Only emits suggestion if the '<' token and '=' token are1708                // directly adjacent (i.e. '=<')1709                if maybe_eq_tok == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {1710                    let eq_lt = maybe_eq_tok.span.to(lt_span);1711                    err.span_suggestion_verbose(1712                        eq_lt,1713                        "you might have meant to write a \"less than or equal to\" comparison",1714                        "<=",1715                        Applicability::Unspecified,1716                    );1717                }1718                err1719            })?;1720            (Some(qself), path)1721        } else {1722            (None, self.parse_path(PathStyle::Expr)?)1723        };17241725        // `!`, as an operator, is prefix, so we know this isn't that.1726        let (span, kind) = if self.eat(exp!(Bang)) {1727            // MACRO INVOCATION expression1728            if qself.is_some() {1729                self.dcx().emit_err(diagnostics::MacroInvocationWithQualifiedPath(path.span));1730            }1731            let lo = path.span;1732            let mac = Box::new(MacCall { path, args: self.parse_delim_args()? });1733            (lo.to(self.prev_token.span), ExprKind::MacCall(mac))1734        } else if self.check(exp!(OpenBrace))1735            && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)1736        {1737            if qself.is_some() {1738                self.psess.gated_spans.gate(sym::more_qualified_paths, path.span);1739            }1740            return expr;1741        } else {1742            (path.span, ExprKind::Path(qself, path))1743        };17441745        let expr = self.mk_expr(span, kind);1746        self.maybe_recover_from_bad_qpath(expr)1747    }17481749    /// Parse `'label: $expr`. The label is already parsed.1750    pub(super) fn parse_expr_labeled(1751        &mut self,1752        label_: Label,1753        mut consume_colon: bool,1754    ) -> PResult<'a, Box<Expr>> {1755        let lo = label_.ident.span;1756        let label = Some(label_);1757        let ate_colon = self.eat(exp!(Colon));1758        let tok_sp = self.token.span;1759        let expr = if self.eat_keyword(exp!(While)) {1760            self.parse_expr_while(label, lo)1761        } else if self.eat_keyword(exp!(For)) {1762            self.parse_expr_for(label, lo)1763        } else if self.eat_keyword(exp!(Loop)) {1764            self.parse_expr_loop(label, lo)1765        } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() {1766            self.parse_expr_block(label, lo, BlockCheckMode::Default)1767        } else if !ate_colon1768            && self.may_recover()1769            && (self.token.kind.close_delim().is_some() || self.token.is_punct())1770            && could_be_unclosed_char_literal(label_.ident)1771        {1772            let (lit, _) =1773                self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {1774                    self_.dcx().create_err(diagnostics::UnexpectedTokenAfterLabel {1775                        span: self_.token.span,1776                        remove_label: None,1777                        enclose_in_block: None,1778                    })1779                });1780            consume_colon = false;1781            Ok(self.mk_expr(lo, ExprKind::Lit(lit)))1782        } else if !ate_colon1783            && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))1784        {1785            // We're probably inside of a `Path<'a>` that needs a turbofish1786            let guar = self.dcx().emit_err(diagnostics::UnexpectedTokenAfterLabel {1787                span: self.token.span,1788                remove_label: None,1789                enclose_in_block: None,1790            });1791            consume_colon = false;1792            Ok(self.mk_expr_err(lo, guar))1793        } else {1794            let mut err = diagnostics::UnexpectedTokenAfterLabel {1795                span: self.token.span,1796                remove_label: None,1797                enclose_in_block: None,1798            };17991800            // Continue as an expression in an effort to recover on `'label: non_block_expr`.1801            let expr = self.parse_expr().map(|expr| {1802                let span = expr.span;18031804                let found_labeled_breaks = {1805                    struct FindLabeledBreaksVisitor;18061807                    impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {1808                        type Result = ControlFlow<()>;1809                        fn visit_expr(&mut self, ex: &'ast Expr) -> ControlFlow<()> {1810                            if let ExprKind::Break(Some(_label), _) = ex.kind {1811                                ControlFlow::Break(())1812                            } else {1813                                walk_expr(self, ex)1814                            }1815                        }1816                    }18171818                    FindLabeledBreaksVisitor.visit_expr(&expr).is_break()1819                };18201821                // Suggestion involves adding a labeled block.1822                //1823                // If there are no breaks that may use this label, suggest removing the label and1824                // recover to the unmodified expression.1825                if !found_labeled_breaks {1826                    err.remove_label = Some(lo.until(span));18271828                    return expr;1829                }18301831                err.enclose_in_block = Some(diagnostics::UnexpectedTokenAfterLabelSugg {1832                    left: span.shrink_to_lo(),1833                    right: span.shrink_to_hi(),1834                });18351836                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.1837                let stmt = self.mk_stmt(span, StmtKind::Expr(expr));1838                let blk = self.mk_block(thin_vec![stmt], BlockCheckMode::Default, span);1839                self.mk_expr(span, ExprKind::Block(blk, label))1840            });18411842            self.dcx().emit_err(err);1843            expr1844        }?;18451846        if !ate_colon && consume_colon {1847            self.dcx().emit_err(diagnostics::RequireColonAfterLabeledExpression {1848                span: expr.span,1849                label: lo,1850                label_end: lo.between(tok_sp),1851            });1852        }18531854        Ok(expr)1855    }18561857    /// Emit an error when a char is parsed as a lifetime or label because of a missing quote.1858    pub(super) fn recover_unclosed_char<L>(1859        &self,1860        ident: Ident,1861        mk_lit_char: impl FnOnce(Symbol, Span) -> L,1862        err: impl FnOnce(&Self) -> Diag<'a>,1863    ) -> L {1864        assert!(could_be_unclosed_char_literal(ident));1865        self.dcx()1866            .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| {1867                err.span_suggestion_verbose(1868                    ident.span.shrink_to_hi(),1869                    "add `'` to close the char literal",1870                    "'",1871                    Applicability::MaybeIncorrect,1872                );1873            })1874            .unwrap_or_else(|| {1875                err(self)1876                    .with_span_suggestion_verbose(1877                        ident.span.shrink_to_hi(),1878                        "add `'` to close the char literal",1879                        "'",1880                        Applicability::MaybeIncorrect,1881                    )1882                    .emit()1883            });1884        let name = ident.without_first_quote().name;1885        mk_lit_char(name, ident.span)1886    }18871888    /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.1889    fn recover_do_catch(&mut self) -> PResult<'a, Box<Expr>> {1890        let lo = self.token.span;18911892        self.bump(); // `do`1893        self.bump(); // `catch`18941895        let span = lo.to(self.prev_token.span);1896        self.dcx().emit_err(diagnostics::DoCatchSyntaxRemoved { span });18971898        self.parse_try_block(lo)1899    }19001901    /// Parse an expression if the token can begin one.1902    fn parse_expr_opt(&mut self) -> PResult<'a, Option<Box<Expr>>> {1903        Ok(if self.token.can_begin_expr() { Some(self.parse_expr()?) } else { None })1904    }19051906    /// Parse `"return" expr?`.1907    fn parse_expr_return(&mut self) -> PResult<'a, Box<Expr>> {1908        let lo = self.prev_token.span;1909        let kind = ExprKind::Ret(self.parse_expr_opt()?);1910        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1911        self.maybe_recover_from_bad_qpath(expr)1912    }19131914    /// Parse `"do" "yeet" expr?`.1915    fn parse_expr_yeet(&mut self) -> PResult<'a, Box<Expr>> {1916        let lo = self.token.span;19171918        self.bump(); // `do`1919        self.bump(); // `yeet`19201921        let kind = ExprKind::Yeet(self.parse_expr_opt()?);19221923        let span = lo.to(self.prev_token.span);1924        self.psess.gated_spans.gate(sym::yeet_expr, span);1925        let expr = self.mk_expr(span, kind);1926        self.maybe_recover_from_bad_qpath(expr)1927    }19281929    /// Parse `"become" expr`, with `"become"` token already eaten.1930    fn parse_expr_become(&mut self) -> PResult<'a, Box<Expr>> {1931        let lo = self.prev_token.span;1932        let kind = ExprKind::Become(self.parse_expr()?);1933        let span = lo.to(self.prev_token.span);1934        self.psess.gated_spans.gate(sym::explicit_tail_calls, span);1935        let expr = self.mk_expr(span, kind);1936        self.maybe_recover_from_bad_qpath(expr)1937    }19381939    /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.1940    /// If the label is followed immediately by a `:` token, the label and `:` are1941    /// parsed as part of the expression (i.e. a labeled loop). The language team has1942    /// decided in #87026 to require parentheses as a visual aid to avoid confusion if1943    /// the break expression of an unlabeled break is a labeled loop (as in1944    /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value1945    /// expression only gets a warning for compatibility reasons; and a labeled break1946    /// with a labeled loop does not even get a warning because there is no ambiguity.1947    fn parse_expr_break(&mut self) -> PResult<'a, Box<Expr>> {1948        let lo = self.prev_token.span;1949        let mut label = self.eat_label();1950        let kind = if self.token == token::Colon1951            && let Some(label) = label.take()1952        {1953            // The value expression can be a labeled loop, see issue #86948, e.g.:1954            // `loop { break 'label: loop { break 'label 42; }; }`1955            let lexpr = self.parse_expr_labeled(label, true)?;1956            self.dcx().emit_err(diagnostics::LabeledLoopInBreak {1957                span: lexpr.span,1958                sub: diagnostics::WrapInParentheses::Expression {1959                    left: lexpr.span.shrink_to_lo(),1960                    right: lexpr.span.shrink_to_hi(),1961                },1962            });1963            Some(lexpr)1964        } else if self.token != token::OpenBrace1965            || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)1966        {1967            let mut expr = self.parse_expr_opt()?;1968            if let Some(expr) = &mut expr {1969                if label.is_some()1970                    && match &expr.kind {1971                        ExprKind::While(_, _, None)1972                        | ExprKind::ForLoop(ForLoop { label: None, .. })1973                        | ExprKind::Loop(_, None, _) => true,1974                        ExprKind::Block(block, None) => {1975                            matches!(block.rules, BlockCheckMode::Default)1976                        }1977                        _ => false,1978                    }1979                {1980                    let span = expr.span;1981                    self.psess.buffer_lint(1982                        BREAK_WITH_LABEL_AND_LOOP,1983                        lo.to(expr.span),1984                        ast::CRATE_NODE_ID,1985                        diagnostics::BreakWithLabelAndLoop {1986                            sub: diagnostics::BreakWithLabelAndLoopSub {1987                                left: span.shrink_to_lo(),1988                                right: span.shrink_to_hi(),1989                            },1990                        },1991                    );1992                }19931994                // Recover `break label aaaaa`1995                if self.may_recover()1996                    && let ExprKind::Path(None, p) = &expr.kind1997                    && let [segment] = &*p.segments1998                    && let &ast::PathSegment { ident, args: None, .. } = segment1999                    && let Some(next) = self.parse_expr_opt()?2000                {

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.