compiler/rustc_parse/src/parser/expr.rs RUST 4,516 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 4,516.
1// ignore-tidy-filelength23use core::mem;4use core::ops::{Bound, ControlFlow};56use ast::mut_visit::{self, MutVisitor};7use ast::token::IdentIsRaw;8use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered};9use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind};10use rustc_ast::tokenstream::TokenTree;11use rustc_ast::util::case::Case;12use rustc_ast::util::classify;13use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};14use rustc_ast::visit::{Visitor, walk_expr};15use rustc_ast::{16    self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,17    BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,18    FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param,19    RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind,20};21use rustc_ast_pretty::pprust;22use rustc_data_structures::stack::ensure_sufficient_stack;23use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};24use rustc_literal_escaper::unescape_char;25use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error};26use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;27use rustc_span::edition::Edition;28use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw, respan, sym};29use thin_vec::{ThinVec, thin_vec};30use tracing::instrument;3132use super::diagnostics::SnapshotParser;33use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};34use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};35use super::{36    AttrWrapper, BlockMode, ClosureSpans, ExpTokenPair, ForceCollect, Parser, PathStyle,37    Restrictions, SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos,38};39use crate::{errors, exp, maybe_recover_from_interpolated_ty_qpath};4041#[derive(Debug)]42pub(super) enum DestructuredFloat {43    /// 1e244    Single(Symbol, Span),45    /// 1.46    TrailingDot(Symbol, Span, Span),47    /// 1.2 | 1.2e348    MiddleDot(Symbol, Span, Span, Symbol, Span),49    /// Invalid50    Error,51}5253impl<'a> Parser<'a> {54    /// Parses an expression.55    #[inline]56    pub fn parse_expr(&mut self) -> PResult<'a, Box<Expr>> {57        self.current_closure.take();5859        let attrs = self.parse_outer_attributes()?;60        self.parse_expr_res(Restrictions::empty(), attrs).map(|res| res.0)61    }6263    /// Parses an expression, forcing tokens to be collected.64    pub fn parse_expr_force_collect(&mut self) -> PResult<'a, Box<Expr>> {65        self.current_closure.take();6667        // If the expression is associative (e.g. `1 + 2`), then any preceding68        // outer attribute actually belongs to the first inner sub-expression.69        // In which case we must use the pre-attr pos to include the attribute70        // in the collected tokens for the outer expression.71        let pre_attr_pos = self.collect_pos();72        let attrs = self.parse_outer_attributes()?;73        self.collect_tokens(74            Some(pre_attr_pos),75            AttrWrapper::empty(),76            ForceCollect::Yes,77            |this, _empty_attrs| {78                let (expr, is_assoc) = this.parse_expr_res(Restrictions::empty(), attrs)?;79                let use_pre_attr_pos =80                    if is_assoc { UsePreAttrPos::Yes } else { UsePreAttrPos::No };81                Ok((expr, Trailing::No, use_pre_attr_pos))82            },83        )84    }8586    pub fn parse_expr_anon_const(87        &mut self,88        mgca_disambiguation: impl FnOnce(&Self, &Expr) -> MgcaDisambiguation,89    ) -> PResult<'a, AnonConst> {90        self.parse_expr().map(|value| AnonConst {91            id: DUMMY_NODE_ID,92            mgca_disambiguation: mgca_disambiguation(self, &value),93            value,94        })95    }9697    fn parse_expr_catch_underscore(98        &mut self,99        restrictions: Restrictions,100    ) -> PResult<'a, Box<Expr>> {101        let attrs = self.parse_outer_attributes()?;102        match self.parse_expr_res(restrictions, attrs) {103            Ok((expr, _)) => Ok(expr),104            Err(err) => match self.token.ident() {105                Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No))106                    if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>107                {108                    // Special-case handling of `foo(_, _, _)`109                    let guar = err.emit();110                    self.bump();111                    Ok(self.mk_expr(self.prev_token.span, ExprKind::Err(guar)))112                }113                _ => Err(err),114            },115        }116    }117118    /// Parses a sequence of expressions delimited by parentheses.119    fn parse_expr_paren_seq(&mut self) -> PResult<'a, ThinVec<Box<Expr>>> {120        self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore(Restrictions::empty()))121            .map(|(r, _)| r)122    }123124    /// Parses an expression, subject to the given restrictions.125    #[inline]126    pub(super) fn parse_expr_res(127        &mut self,128        r: Restrictions,129        attrs: AttrWrapper,130    ) -> PResult<'a, (Box<Expr>, bool)> {131        self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs))132    }133134    /// Parses an associative expression with operators of at least `min_prec` precedence.135    /// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator136    /// followed by a subexpression (e.g. `1 + 2`).137    pub(super) fn parse_expr_assoc_with(138        &mut self,139        min_prec: Bound<ExprPrecedence>,140        attrs: AttrWrapper,141    ) -> PResult<'a, (Box<Expr>, bool)> {142        let lhs = if self.token.is_range_separator() {143            return self.parse_expr_prefix_range(attrs).map(|res| (res, false));144        } else {145            self.parse_expr_prefix(attrs)?146        };147        self.parse_expr_assoc_rest_with(min_prec, false, lhs)148    }149150    /// Parses the rest of an associative expression (i.e. the part after the lhs) with operators151    /// of at least `min_prec` precedence. The `bool` in the return value indicates if something152    /// was actually parsed.153    pub(super) fn parse_expr_assoc_rest_with(154        &mut self,155        min_prec: Bound<ExprPrecedence>,156        starts_stmt: bool,157        mut lhs: Box<Expr>,158    ) -> PResult<'a, (Box<Expr>, bool)> {159        let mut parsed_something = false;160        if !self.should_continue_as_assoc_expr(&lhs) {161            return Ok((lhs, parsed_something));162        }163164        self.expected_token_types.insert(TokenType::Operator);165        while let Some(op) = self.check_assoc_op() {166            let lhs_span = self.interpolated_or_expr_span(&lhs);167            let cur_op_span = self.token.span;168            let restrictions = if op.node.is_assign_like() {169                self.restrictions & Restrictions::NO_STRUCT_LITERAL170            } else {171                self.restrictions172            };173            let prec = op.node.precedence();174            if match min_prec {175                Bound::Included(min_prec) => prec < min_prec,176                Bound::Excluded(min_prec) => prec <= min_prec,177                Bound::Unbounded => false,178            } {179                break;180            }181            // Check for deprecated `...` syntax182            if self.token == token::DotDotDot && op.node == AssocOp::Range(RangeLimits::Closed) {183                self.err_dotdotdot_syntax(self.token.span);184            }185186            if self.token == token::LArrow {187                self.err_larrow_operator(self.token.span);188            }189190            parsed_something = true;191            self.bump();192            if op.node.is_comparison() {193                if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {194                    return Ok((expr, parsed_something));195                }196            }197198            // Look for JS' `===` and `!==` and recover199            if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node200                && self.token == token::Eq201                && self.prev_token.span.hi() == self.token.span.lo()202            {203                let sp = op.span.to(self.token.span);204                let sugg = bop.as_str().into();205                let invalid = format!("{sugg}=");206                self.dcx().emit_err(errors::InvalidComparisonOperator {207                    span: sp,208                    invalid: invalid.clone(),209                    sub: errors::InvalidComparisonOperatorSub::Correctable {210                        span: sp,211                        invalid,212                        correct: sugg,213                    },214                });215                self.bump();216            }217218            // Look for PHP's `<>` and recover219            if op.node == AssocOp::Binary(BinOpKind::Lt)220                && self.token == token::Gt221                && self.prev_token.span.hi() == self.token.span.lo()222            {223                let sp = op.span.to(self.token.span);224                self.dcx().emit_err(errors::InvalidComparisonOperator {225                    span: sp,226                    invalid: "<>".into(),227                    sub: errors::InvalidComparisonOperatorSub::Correctable {228                        span: sp,229                        invalid: "<>".into(),230                        correct: "!=".into(),231                    },232                });233                self.bump();234            }235236            // Look for C++'s `<=>` and recover237            if op.node == AssocOp::Binary(BinOpKind::Le)238                && self.token == token::Gt239                && self.prev_token.span.hi() == self.token.span.lo()240            {241                let sp = op.span.to(self.token.span);242                self.dcx().emit_err(errors::InvalidComparisonOperator {243                    span: sp,244                    invalid: "<=>".into(),245                    sub: errors::InvalidComparisonOperatorSub::Spaceship(sp),246                });247                self.bump();248            }249250            if self.prev_token == token::Plus251                && self.token == token::Plus252                && self.prev_token.span.between(self.token.span).is_empty()253            {254                let op_span = self.prev_token.span.to(self.token.span);255                // Eat the second `+`256                self.bump();257                lhs = self.recover_from_postfix_increment(lhs, op_span, starts_stmt)?;258                continue;259            }260261            if self.prev_token == token::Minus262                && self.token == token::Minus263                && self.prev_token.span.between(self.token.span).is_empty()264                && !self.look_ahead(1, |tok| tok.can_begin_expr())265            {266                let op_span = self.prev_token.span.to(self.token.span);267                // Eat the second `-`268                self.bump();269                lhs = self.recover_from_postfix_decrement(lhs, op_span, starts_stmt)?;270                continue;271            }272273            let op_span = op.span;274            let op = op.node;275            // Special cases:276            if op == AssocOp::Cast {277                lhs = self.parse_assoc_op_cast(lhs, lhs_span, op_span, ExprKind::Cast)?;278                continue;279            } else if let AssocOp::Range(limits) = op {280                // If we didn't have to handle `x..`/`x..=`, it would be pretty easy to281                // generalise it to the Fixity::None code.282                lhs = self.parse_expr_range(prec, lhs, limits, cur_op_span)?;283                break;284            }285286            let min_prec = match op.fixity() {287                Fixity::Right => Bound::Included(prec),288                Fixity::Left | Fixity::None => Bound::Excluded(prec),289            };290            let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {291                let attrs = this.parse_outer_attributes()?;292                this.parse_expr_assoc_with(min_prec, attrs)293            })?;294295            let span = self.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span);296            lhs = match op {297                AssocOp::Binary(ast_op) => {298                    let binary = self.mk_binary(respan(cur_op_span, ast_op), lhs, rhs);299                    self.mk_expr(span, binary)300                }301                AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)),302                AssocOp::AssignOp(aop) => {303                    let aopexpr = self.mk_assign_op(respan(cur_op_span, aop), lhs, rhs);304                    self.mk_expr(span, aopexpr)305                }306                AssocOp::Cast | AssocOp::Range(_) => {307                    self.dcx().span_bug(span, "AssocOp should have been handled by special case")308                }309            };310        }311312        Ok((lhs, parsed_something))313    }314315    fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {316        match (self.expr_is_complete(lhs), AssocOp::from_token(&self.token)) {317            // Semi-statement forms are odd:318            // See https://github.com/rust-lang/rust/issues/29071319            (true, None) => false,320            (false, _) => true, // Continue parsing the expression.321            // An exhaustive check is done in the following block, but these are checked first322            // because they *are* ambiguous but also reasonable looking incorrect syntax, so we323            // want to keep their span info to improve diagnostics in these cases in a later stage.324            (true, Some(AssocOp::Binary(325                BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3`326                BinOpKind::Sub | // `{ 42 } -5`327                BinOpKind::Add | // `{ 42 } + 42` (unary plus)328                BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`329                BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure)330                BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42`331            ))) => {332                // These cases are ambiguous and can't be identified in the parser alone.333                //334                // Bitwise AND is left out because guessing intent is hard. We can make335                // suggestions based on the assumption that double-refs are rarely intentional,336                // and closures are distinct enough that they don't get mixed up with their337                // return value.338                let sp = self.psess.source_map().start_point(self.token.span);339                self.psess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);340                false341            }342            (true, Some(op)) if !op.can_continue_expr_unambiguously() => false,343            (true, Some(_)) => {344                self.error_found_expr_would_be_stmt(lhs);345                true346            }347        }348    }349350    /// We've found an expression that would be parsed as a statement,351    /// but the next token implies this should be parsed as an expression.352    /// For example: `if let Some(x) = x { x } else { 0 } / 2`.353    fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {354        self.dcx().emit_err(errors::FoundExprWouldBeStmt {355            span: self.token.span,356            token: pprust::token_to_string(&self.token),357            suggestion: ExprParenthesesNeeded::surrounding(lhs.span),358        });359    }360361    /// Possibly translate the current token to an associative operator.362    /// The method does not advance the current token.363    ///364    /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.365    pub(super) fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {366        let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) {367            // When parsing const expressions, stop parsing when encountering `>`.368            (369                Some(370                    AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)371                    | AssocOp::AssignOp(AssignOpKind::ShrAssign),372                ),373                _,374            ) if self.restrictions.contains(Restrictions::CONST_EXPR) => {375                return None;376            }377            // When recovering patterns as expressions, stop parsing when encountering an378            // assignment `=`, an alternative `|`, or a range `..`.379            (380                Some(381                    AssocOp::Assign382                    | AssocOp::AssignOp(_)383                    | AssocOp::Binary(BinOpKind::BitOr)384                    | AssocOp::Range(_),385                ),386                _,387            ) if self.restrictions.contains(Restrictions::IS_PAT) => {388                return None;389            }390            (Some(op), _) => (op, self.token.span),391            (None, Some((Ident { name: sym::and, span }, IdentIsRaw::No)))392                if self.may_recover() =>393            {394                self.dcx().emit_err(errors::InvalidLogicalOperator {395                    span: self.token.span,396                    incorrect: "and".into(),397                    sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span),398                });399                (AssocOp::Binary(BinOpKind::And), span)400            }401            (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => {402                self.dcx().emit_err(errors::InvalidLogicalOperator {403                    span: self.token.span,404                    incorrect: "or".into(),405                    sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span),406                });407                (AssocOp::Binary(BinOpKind::Or), span)408            }409            _ => return None,410        };411        Some(respan(span, op))412    }413414    /// Checks if this expression is a successfully parsed statement.415    fn expr_is_complete(&self, e: &Expr) -> bool {416        self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)417    }418419    /// Parses `x..y`, `x..=y`, and `x..`/`x..=`.420    /// The other two variants are handled in `parse_prefix_range_expr` below.421    fn parse_expr_range(422        &mut self,423        prec: ExprPrecedence,424        lhs: Box<Expr>,425        limits: RangeLimits,426        cur_op_span: Span,427    ) -> PResult<'a, Box<Expr>> {428        let rhs = if self.is_at_start_of_range_notation_rhs() {429            let maybe_lt = self.token;430            let attrs = self.parse_outer_attributes()?;431            Some(432                self.parse_expr_assoc_with(Bound::Excluded(prec), attrs)433                    .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?434                    .0,435            )436        } else {437            None438        };439        let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);440        let span = self.mk_expr_sp(&lhs, lhs.span, cur_op_span, rhs_span);441        let range = self.mk_range(Some(lhs), rhs, limits);442        Ok(self.mk_expr(span, range))443    }444445    fn is_at_start_of_range_notation_rhs(&self) -> bool {446        if self.token.can_begin_expr() {447            // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.448            if self.token == token::OpenBrace {449                return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);450            }451            true452        } else {453            false454        }455    }456457    /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`.458    fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {459        if !attrs.is_empty() {460            let err = errors::DotDotRangeAttribute { span: self.token.span };461            self.dcx().emit_err(err);462        }463464        // Check for deprecated `...` syntax.465        if self.token == token::DotDotDot {466            self.err_dotdotdot_syntax(self.token.span);467        }468469        debug_assert!(470            self.token.is_range_separator(),471            "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq",472            self.token473        );474475        let limits = match self.token.kind {476            token::DotDot => RangeLimits::HalfOpen,477            _ => RangeLimits::Closed,478        };479        let op = AssocOp::from_token(&self.token);480        let attrs = self.parse_outer_attributes()?;481        self.collect_tokens_for_expr(attrs, |this, attrs| {482            let lo = this.token.span;483            let maybe_lt = this.look_ahead(1, |t| t.clone());484            this.bump();485            let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {486                // RHS must be parsed with more associativity than the dots.487                let attrs = this.parse_outer_attributes()?;488                this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs)489                    .map(|(x, _)| (lo.to(x.span), Some(x)))490                    .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?491            } else {492                (lo, None)493            };494            let range = this.mk_range(None, opt_end, limits);495            Ok(this.mk_expr_with_attrs(span, range, attrs))496        })497    }498499    /// Parses a prefix-unary-operator expr.500    fn parse_expr_prefix(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {501        let lo = self.token.span;502503        macro_rules! make_it {504            ($this:ident, $attrs:expr, |this, _| $body:expr) => {505                $this.collect_tokens_for_expr($attrs, |$this, attrs| {506                    let (hi, ex) = $body?;507                    Ok($this.mk_expr_with_attrs(lo.to(hi), ex, attrs))508                })509            };510        }511512        let this = self;513514        // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()515        match this.token.uninterpolate().kind {516            // `!expr`517            token::Bang => make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Not)),518            // `~expr`519            token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)),520            // `-expr`521            token::Minus => {522                make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Neg))523            }524            // `*expr`525            token::Star => {526                make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Deref))527            }528            // `&expr` and `&&expr`529            token::And | token::AndAnd => {530                make_it!(this, attrs, |this, _| this.parse_expr_borrow(lo))531            }532            // `+lit`533            token::Plus if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {534                let mut err = errors::LeadingPlusNotSupported {535                    span: lo,536                    remove_plus: None,537                    add_parentheses: None,538                };539540                // a block on the LHS might have been intended to be an expression instead541                if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {542                    err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp));543                } else {544                    err.remove_plus = Some(lo);545                }546                this.dcx().emit_err(err);547548                this.bump();549                let attrs = this.parse_outer_attributes()?;550                this.parse_expr_prefix(attrs)551            }552            // Recover from `++x`:553            token::Plus if this.look_ahead(1, |t| *t == token::Plus) => {554                let starts_stmt =555                    this.prev_token == token::Semi || this.prev_token == token::CloseBrace;556                let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span));557                // Eat both `+`s.558                this.bump();559                this.bump();560561                let operand_expr = this.parse_expr_dot_or_call(attrs)?;562                this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt)563            }564            token::Ident(..) if this.token.is_keyword(kw::Box) => {565                make_it!(this, attrs, |this, _| this.parse_expr_box(lo))566            }567            token::Ident(..)568                if this.token.is_keyword(kw::Move)569                    && this.look_ahead(1, |t| *t == token::OpenParen) =>570            {571                make_it!(this, attrs, |this, _| this.parse_expr_move(lo))572            }573            token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {574                make_it!(this, attrs, |this, _| this.recover_not_expr(lo))575            }576            _ => return this.parse_expr_dot_or_call(attrs),577        }578    }579580    fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, Box<Expr>)> {581        self.bump();582        let attrs = self.parse_outer_attributes()?;583        let expr = if self.token.is_range_separator() {584            self.parse_expr_prefix_range(attrs)585        } else {586            self.parse_expr_prefix(attrs)587        }?;588        let span = self.interpolated_or_expr_span(&expr);589        Ok((lo.to(span), expr))590    }591592    fn parse_expr_unary(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> {593        let (span, expr) = self.parse_expr_prefix_common(lo)?;594        Ok((span, self.mk_unary(op, expr)))595    }596597    /// Recover on `~expr` in favor of `!expr`.598    fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {599        self.dcx().emit_err(errors::TildeAsUnaryOperator(lo));600601        self.parse_expr_unary(lo, UnOp::Not)602    }603604    /// Parse `box expr` - this syntax has been removed, but we still parse this605    /// for now to provide a more useful error606    fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {607        let (span, expr) = self.parse_expr_prefix_common(box_kw)?;608        // Make a multipart suggestion instead of `span_to_snippet` in case source isn't available609        let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));610        let hi = span.shrink_to_hi();611        let sugg = errors::AddBoxNew { box_kw_and_lo, hi };612        let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span, sugg });613        Ok((span, ExprKind::Err(guar)))614    }615616    fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> {617        self.bump();618        self.psess.gated_spans.gate(sym::move_expr, move_kw);619        self.expect(exp!(OpenParen))?;620        let expr = self.parse_expr()?;621        self.expect(exp!(CloseParen))?;622        let span = move_kw.to(self.prev_token.span);623        Ok((span, ExprKind::Move(expr, move_kw)))624    }625626    fn is_mistaken_not_ident_negation(&self) -> bool {627        let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {628            // These tokens can start an expression after `!`, but629            // can't continue an expression after an ident630            token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),631            token::Literal(..) | token::Pound => true,632            _ => t.is_metavar_expr(),633        };634        self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr)635    }636637    /// Recover on `not expr` in favor of `!expr`.638    fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {639        let negated_token = self.look_ahead(1, |t| *t);640641        let sub_diag = if negated_token.is_numeric_lit() {642            errors::NotAsNegationOperatorSub::SuggestNotBitwise643        } else if negated_token.is_bool_lit() {644            errors::NotAsNegationOperatorSub::SuggestNotLogical645        } else {646            errors::NotAsNegationOperatorSub::SuggestNotDefault647        };648649        self.dcx().emit_err(errors::NotAsNegationOperator {650            negated: negated_token.span,651            negated_desc: super::token_descr(&negated_token),652            // Span the `not` plus trailing whitespace to avoid653            // trailing whitespace after the `!` in our suggestion654            sub: sub_diag(655                self.psess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),656            ),657        });658659        self.parse_expr_unary(lo, UnOp::Not)660    }661662    /// Returns the span of expr if it was not interpolated, or the span of the interpolated token.663    fn interpolated_or_expr_span(&self, expr: &Expr) -> Span {664        match self.prev_token.kind {665            token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span,666            token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => {667                // `expr.span` is the interpolated span, because invisible open668                // and close delims both get marked with the same span, one669                // that covers the entire thing between them. (See670                // `rustc_expand::mbe::transcribe::transcribe`.)671                self.prev_token.span672            }673            _ => expr.span,674        }675    }676677    fn parse_assoc_op_cast(678        &mut self,679        lhs: Box<Expr>,680        lhs_span: Span,681        op_span: Span,682        expr_kind: fn(Box<Expr>, Box<Ty>) -> ExprKind,683    ) -> PResult<'a, Box<Expr>> {684        let mk_expr = |this: &mut Self, lhs: Box<Expr>, rhs: Box<Ty>| {685            this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs))686        };687688        // Save the state of the parser before parsing type normally, in case there is a689        // LessThan comparison after this cast.690        let parser_snapshot_before_type = self.clone();691        let cast_expr = match self.parse_as_cast_ty() {692            Ok(rhs) => mk_expr(self, lhs, rhs),693            Err(type_err) => {694                if !self.may_recover() {695                    return Err(type_err);696                }697698                // Rewind to before attempting to parse the type with generics, to recover699                // from situations like `x as usize < y` in which we first tried to parse700                // `usize < y` as a type with generic arguments.701                let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type);702703                // Check for typo of `'a: loop { break 'a }` with a missing `'`.704                match (&lhs.kind, &self.token.kind) {705                    (706                        // `foo: `707                        ExprKind::Path(None, ast::Path { segments, .. }),708                        token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No),709                    ) if let [segment] = segments.as_slice() => {710                        let snapshot = self.create_snapshot_for_diagnostic();711                        let label = Label {712                            ident: Ident::from_str_and_span(713                                &format!("'{}", segment.ident),714                                segment.ident.span,715                            ),716                        };717                        match self.parse_expr_labeled(label, false) {718                            Ok(expr) => {719                                type_err.cancel();720                                self.dcx().emit_err(errors::MalformedLoopLabel {721                                    span: label.ident.span,722                                    suggestion: label.ident.span.shrink_to_lo(),723                                });724                                return Ok(expr);725                            }726                            Err(err) => {727                                err.cancel();728                                self.restore_snapshot(snapshot);729                            }730                        }731                    }732                    _ => {}733                }734735                match self.parse_path(PathStyle::Expr) {736                    Ok(path) => {737                        let span_after_type = parser_snapshot_after_type.token.span;738                        let expr = mk_expr(739                            self,740                            lhs,741                            self.mk_ty(path.span, TyKind::Path(None, path.clone())),742                        );743744                        let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);745                        match self.token.kind {746                            token::Lt => {747                                self.dcx().emit_err(errors::ComparisonInterpretedAsGeneric {748                                    comparison: self.token.span,749                                    r#type: pprust::path_to_string(&path),750                                    args: args_span,751                                    suggestion: errors::ComparisonInterpretedAsGenericSugg {752                                        left: expr.span.shrink_to_lo(),753                                        right: expr.span.shrink_to_hi(),754                                    },755                                })756                            }757                            token::Shl => self.dcx().emit_err(errors::ShiftInterpretedAsGeneric {758                                shift: self.token.span,759                                r#type: pprust::path_to_string(&path),760                                args: args_span,761                                suggestion: errors::ShiftInterpretedAsGenericSugg {762                                    left: expr.span.shrink_to_lo(),763                                    right: expr.span.shrink_to_hi(),764                                },765                            }),766                            _ => {767                                // We can end up here even without `<` being the next token, for768                                // example because `parse_ty_no_plus` returns `Err` on keywords,769                                // but `parse_path` returns `Ok` on them due to error recovery.770                                // Return original error and parser state.771                                *self = parser_snapshot_after_type;772                                return Err(type_err);773                            }774                        };775776                        // Successfully parsed the type path leaving a `<` yet to parse.777                        type_err.cancel();778779                        // Keep `x as usize` as an expression in AST and continue parsing.780                        expr781                    }782                    Err(path_err) => {783                        // Couldn't parse as a path, return original error and parser state.784                        path_err.cancel();785                        *self = parser_snapshot_after_type;786                        return Err(type_err);787                    }788                }789            }790        };791792        // Try to parse a postfix operator such as `.`, `?`, or index (`[]`)793        // after a cast. If one is present, emit an error then return a valid794        // parse tree; For something like `&x as T[0]` will be as if it was795        // written `((&x) as T)[0]`.796797        let span = cast_expr.span;798799        let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?;800801        // Check if an illegal postfix operator has been added after the cast.802        // If the resulting expression is not a cast, it is an illegal postfix operator.803        if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {804            let msg = format!(805                "cast cannot be followed by {}",806                match with_postfix.kind {807                    ExprKind::Index(..) => "indexing",808                    ExprKind::Try(_) => "`?`",809                    ExprKind::Field(_, _) => "a field access",810                    ExprKind::MethodCall(_) => "a method call",811                    ExprKind::Call(_, _) => "a function call",812                    ExprKind::Await(_, _) => "`.await`",813                    ExprKind::Use(_, _) => "`.use`",814                    ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`",815                    ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",816                    ExprKind::Err(_) => return Ok(with_postfix),817                    _ => unreachable!(818                        "did not expect {:?} as an illegal postfix operator following cast",819                        with_postfix.kind820                    ),821                }822            );823            let mut err = self.dcx().struct_span_err(span, msg);824825            let suggest_parens = |err: &mut Diag<'_>| {826                let suggestions = vec![827                    (span.shrink_to_lo(), "(".to_string()),828                    (span.shrink_to_hi(), ")".to_string()),829                ];830                err.multipart_suggestion(831                    "try surrounding the expression in parentheses",832                    suggestions,833                    Applicability::MachineApplicable,834                );835            };836837            suggest_parens(&mut err);838839            err.emit();840        };841        Ok(with_postfix)842    }843844    /// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.845    fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {846        self.expect_and()?;847        let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);848        let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.849        let (borrow_kind, mutbl) = self.parse_borrow_modifiers();850        let attrs = self.parse_outer_attributes()?;851        let expr = if self.token.is_range_separator() {852            self.parse_expr_prefix_range(attrs)853        } else {854            self.parse_expr_prefix(attrs)855        }?;856        let hi = self.interpolated_or_expr_span(&expr);857        let span = lo.to(hi);858        if let Some(lt) = lifetime {859            self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));860        }861862        // Add expected tokens if we parsed `&raw` as an expression.863        // This will make sure we see "expected `const`, `mut`", and864        // guides recovery in case we write `&raw expr`.865        if borrow_kind == ast::BorrowKind::Ref866            && mutbl == ast::Mutability::Not867            && matches!(&expr.kind, ExprKind::Path(None, p) if *p == kw::Raw)868        {869            self.expected_token_types.insert(TokenType::KwMut);870            self.expected_token_types.insert(TokenType::KwConst);871        }872873        Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))874    }875876    fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {877        self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });878    }879880    /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.881    fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {882        if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {883            // `raw [ const | mut ]`.884            let found_raw = self.eat_keyword(exp!(Raw));885            assert!(found_raw);886            let mutability = self.parse_mut_or_const().unwrap();887            (ast::BorrowKind::Raw, mutability)888        } else {889            match self.parse_pin_and_mut() {890                // `mut?`891                (ast::Pinnedness::Not, mutbl) => (ast::BorrowKind::Ref, mutbl),892                // `pin [ const | mut ]`.893                // `pin` has been gated in `self.parse_pin_and_mut()` so we don't894                // need to gate it here.895                (ast::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl),896            }897        }898    }899900    /// Parses `a.b` or `a(13)` or `a[4]` or just `a`.901    fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {902        self.collect_tokens_for_expr(attrs, |this, attrs| {903            let base = this.parse_expr_bottom()?;904            let span = this.interpolated_or_expr_span(&base);905            this.parse_expr_dot_or_call_with(attrs, base, span)906        })907    }908909    pub(super) fn parse_expr_dot_or_call_with(910        &mut self,911        mut attrs: ast::AttrVec,912        mut e: Box<Expr>,913        lo: Span,914    ) -> PResult<'a, Box<Expr>> {915        let mut res = ensure_sufficient_stack(|| {916            loop {917                let has_question =918                    if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {919                        // We are using noexpect here because we don't expect a `?` directly after920                        // a `return` which could be suggested otherwise.921                        self.eat_noexpect(&token::Question)922                    } else {923                        self.eat(exp!(Question))924                    };925                if has_question {926                    // `expr?`927                    e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e));928                    continue;929                }930                let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {931                    // We are using noexpect here because we don't expect a `.` directly after932                    // a `return` which could be suggested otherwise.933                    self.eat_noexpect(&token::Dot)934                } else if self.token == TokenKind::RArrow && self.may_recover() {935                    // Recovery for `expr->suffix`.936                    self.bump();937                    let span = self.prev_token.span;938                    self.dcx().emit_err(errors::ExprRArrowCall { span });939                    true940                } else {941                    self.eat(exp!(Dot))942                };943                if has_dot {944                    // expr.f945                    e = self.parse_dot_suffix_expr(lo, e)?;946                    continue;947                }948                if self.expr_is_complete(&e) {949                    return Ok(e);950                }951                e = match self.token.kind {952                    token::OpenParen => self.parse_expr_fn_call(lo, e),953                    token::OpenBracket => self.parse_expr_index(lo, e)?,954                    _ => return Ok(e),955                }956            }957        });958959        // Stitch the list of outer attributes onto the return value. A little960        // bit ugly, but the best way given the current code structure.961        if !attrs.is_empty()962            && let Ok(expr) = &mut res963        {964            mem::swap(&mut expr.attrs, &mut attrs);965            expr.attrs.extend(attrs)966        }967        res968    }969970    pub(super) fn parse_dot_suffix_expr(971        &mut self,972        lo: Span,973        base: Box<Expr>,974    ) -> PResult<'a, Box<Expr>> {975        // At this point we've consumed something like `expr.` and `self.token` holds the token976        // after the dot.977        match self.token.uninterpolate().kind {978            token::Ident(..) => self.parse_dot_suffix(base, lo),979            token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {980                let ident_span = self.token.span;981                self.bump();982                Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix))983            }984            token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => {985                Ok(match self.break_up_float(symbol, self.token.span) {986                    // 1e2987                    DestructuredFloat::Single(sym, _sp) => {988                        // `foo.1e2`: a single complete dot access, fully consumed. We end up with989                        // the `1e2` token in `self.prev_token` and the following token in990                        // `self.token`.991                        let ident_span = self.token.span;992                        self.bump();993                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix)994                    }995                    // 1.996                    DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {997                        // `foo.1.`: a single complete dot access and the start of another.998                        // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in999                        // `self.token`.1000                        assert!(suffix.is_none());1001                        self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span);1002                        self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));1003                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None)1004                    }1005                    // 1.2 | 1.2e31006                    DestructuredFloat::MiddleDot(1007                        sym1,1008                        ident1_span,1009                        _dot_span,1010                        sym2,1011                        ident2_span,1012                    ) => {1013                        // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with1014                        // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following1015                        // token in `self.token`.1016                        let next_token2 =1017                            Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span);1018                        self.bump_with((next_token2, self.token_spacing));1019                        self.bump();1020                        let base1 =1021                            self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None);1022                        self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix)1023                    }1024                    DestructuredFloat::Error => base,1025                })1026            }1027            _ => {1028                self.error_unexpected_after_dot();1029                Ok(base)1030            }1031        }1032    }10331034    fn error_unexpected_after_dot(&self) {1035        let actual = super::token_descr(&self.token);1036        let span = self.token.span;1037        let sm = self.psess.source_map();1038        let (span, actual) = match (&self.token.kind, self.subparser_name) {1039            (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => {1040                (span.shrink_to_hi(), format!("`{}`", snippet))1041            }1042            (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => {1043                // No need to report an error. This case will only occur when parsing a pasted1044                // metavariable, and we should have emitted an error when parsing the macro call in1045                // the first place. E.g. in this code:1046                // ```1047                // macro_rules! m { ($e:expr) => { $e }; }1048                //1049                // fn main() {1050                //     let f = 1;1051                //     m!(f.);1052                // }1053                // ```1054                // we'll get an error "unexpected token: `)` when parsing the `m!(f.)`, so we don't1055                // want to issue a second error when parsing the expansion `«f.»` (where `«`/`»`1056                // represent the invisible delimiters).1057                self.dcx().span_delayed_bug(span, "bad dot expr in metavariable");1058                return;1059            }1060            _ => (span, actual),1061        };1062        self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual });1063    }10641065    /// We need an identifier or integer, but the next token is a float.1066    /// Break the float into components to extract the identifier or integer.1067    ///1068    /// See also [`TokenKind::break_two_token_op`] which does similar splitting of `>>` into `>`.1069    //1070    // FIXME: With current `TokenCursor` it's hard to break tokens into more than 21071    //  parts unless those parts are processed immediately. `TokenCursor` should either1072    //  support pushing "future tokens" (would be also helpful to `break_and_eat`), or1073    //  we should break everything including floats into more basic proc-macro style1074    //  tokens in the lexer (probably preferable).1075    pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {1076        #[derive(Debug)]1077        enum FloatComponent {1078            IdentLike(String),1079            Punct(char),1080        }1081        use FloatComponent::*;10821083        let float_str = float.as_str();1084        let mut components = Vec::new();1085        let mut ident_like = String::new();1086        for c in float_str.chars() {1087            if c == '_' || c.is_ascii_alphanumeric() {1088                ident_like.push(c);1089            } else if matches!(c, '.' | '+' | '-') {1090                if !ident_like.is_empty() {1091                    components.push(IdentLike(mem::take(&mut ident_like)));1092                }1093                components.push(Punct(c));1094            } else {1095                panic!("unexpected character in a float token: {c:?}")1096            }1097        }1098        if !ident_like.is_empty() {1099            components.push(IdentLike(ident_like));1100        }11011102        // With proc macros the span can refer to anything, the source may be too short,1103        // or too long, or non-ASCII. It only makes sense to break our span into components1104        // if its underlying text is identical to our float literal.1105        let can_take_span_apart =1106            || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref();11071108        match &*components {1109            // 1e21110            [IdentLike(i)] => DestructuredFloat::Single(Symbol::intern(i), span),1111            // 1.1112            [IdentLike(left), Punct('.')] => {1113                let (left_span, dot_span) = if can_take_span_apart() {1114                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1115                    let dot_span = span.with_lo(left_span.hi());1116                    (left_span, dot_span)1117                } else {1118                    (span, span)1119                };1120                let left = Symbol::intern(left);1121                DestructuredFloat::TrailingDot(left, left_span, dot_span)1122            }1123            // 1.2 | 1.2e31124            [IdentLike(left), Punct('.'), IdentLike(right)] => {1125                let (left_span, dot_span, right_span) = if can_take_span_apart() {1126                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1127                    let dot_span =1128                        span.with_lo(left_span.hi()).with_hi(left_span.hi() + BytePos(1));1129                    let right_span = span.with_lo(dot_span.hi());1130                    (left_span, dot_span, right_span)1131                } else {1132                    (span, span, span)1133                };1134                let left = Symbol::intern(left);1135                let right = Symbol::intern(right);1136                DestructuredFloat::MiddleDot(left, left_span, dot_span, right, right_span)1137            }1138            // 1e+ | 1e- (recovered)1139            [IdentLike(_), Punct('+' | '-')] |1140            // 1e+2 | 1e-21141            [IdentLike(_), Punct('+' | '-'), IdentLike(_)] |1142            // 1.2e+ | 1.2e-1143            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] |1144            // 1.2e+3 | 1.2e-31145            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {1146                // See the FIXME about `TokenCursor` above.1147                self.error_unexpected_after_dot();1148                DestructuredFloat::Error1149            }1150            _ => panic!("unexpected components in a float token: {components:?}"),1151        }1152    }11531154    /// Parse the field access used in offset_of, matched by `$(e:expr)+`.1155    /// Currently returns a list of idents. However, it should be possible in1156    /// future to also do array indices, which might be arbitrary expressions.1157    pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {1158        let mut fields = Vec::new();1159        let mut trailing_dot = None;11601161        loop {1162            // This is expected to use a metavariable $(args:expr)+, but the builtin syntax1163            // could be called directly. Calling `parse_expr` allows this function to only1164            // consider `Expr`s.1165            let expr = self.parse_expr()?;1166            let mut current = &expr;1167            let start_idx = fields.len();1168            loop {1169                match current.kind {1170                    ExprKind::Field(ref left, right) => {1171                        // Field access is read right-to-left.1172                        fields.insert(start_idx, right);1173                        trailing_dot = None;1174                        current = left;1175                    }1176                    // Parse this both to give helpful error messages and to1177                    // verify it can be done with this parser setup.1178                    ExprKind::Index(ref left, ref _right, span) => {1179                        self.dcx().emit_err(errors::ArrayIndexInOffsetOf(span));1180                        current = left;1181                    }1182                    ExprKind::Lit(token::Lit {1183                        kind: token::Float | token::Integer,1184                        symbol,1185                        suffix,1186                    }) => {1187                        if let Some(suffix) = suffix {1188                            self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {1189                                span: current.span,1190                                suffix,1191                            });1192                        }1193                        match self.break_up_float(symbol, current.span) {1194                            // 1e21195                            DestructuredFloat::Single(sym, sp) => {1196                                trailing_dot = None;1197                                fields.insert(start_idx, Ident::new(sym, sp));1198                            }1199                            // 1.1200                            DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {1201                                assert!(suffix.is_none());1202                                trailing_dot = Some(dot_span);1203                                fields.insert(start_idx, Ident::new(sym, sym_span));1204                            }1205                            // 1.2 | 1.2e31206                            DestructuredFloat::MiddleDot(1207                                symbol1,1208                                span1,1209                                _dot_span,1210                                symbol2,1211                                span2,1212                            ) => {1213                                trailing_dot = None;1214                                fields.insert(start_idx, Ident::new(symbol2, span2));1215                                fields.insert(start_idx, Ident::new(symbol1, span1));1216                            }1217                            DestructuredFloat::Error => {1218                                trailing_dot = None;1219                                fields.insert(start_idx, Ident::new(symbol, self.prev_token.span));1220                            }1221                        }1222                        break;1223                    }1224                    ExprKind::Path(None, Path { ref segments, .. }) => {1225                        match &segments[..] {1226                            [PathSegment { ident, args: None, .. }] => {1227                                trailing_dot = None;1228                                fields.insert(start_idx, *ident)1229                            }1230                            _ => {1231                                self.dcx().emit_err(errors::InvalidOffsetOf(current.span));1232                                break;1233                            }1234                        }1235                        break;1236                    }1237                    _ => {1238                        self.dcx().emit_err(errors::InvalidOffsetOf(current.span));1239                        break;1240                    }1241                }1242            }12431244            if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma {1245                break;1246            } else if trailing_dot.is_none() {1247                // This loop should only repeat if there is a trailing dot.1248                self.dcx().emit_err(errors::InvalidOffsetOf(self.token.span));1249                break;1250            }1251        }1252        if let Some(dot) = trailing_dot {1253            self.dcx().emit_err(errors::InvalidOffsetOf(dot));1254        }1255        Ok(fields.into_iter().collect())1256    }12571258    fn mk_expr_tuple_field_access(1259        &self,1260        lo: Span,1261        ident_span: Span,1262        base: Box<Expr>,1263        field: Symbol,1264        suffix: Option<Symbol>,1265    ) -> Box<Expr> {1266        if let Some(suffix) = suffix {1267            self.dcx()1268                .emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix });1269        }1270        self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span)))1271    }12721273    /// Parse a function call expression, `expr(...)`.1274    fn parse_expr_fn_call(&mut self, lo: Span, fun: Box<Expr>) -> Box<Expr> {1275        let snapshot = if self.token == token::OpenParen {1276            Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))1277        } else {1278            None1279        };1280        let open_paren = self.token.span;1281        let call_depth = self.token_cursor.stack.len();12821283        let seq = match self.parse_expr_paren_seq() {1284            Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))),1285            Err(err)1286                if self.is_expected_raw_ref_mut()1287                    && self.token_cursor.stack.len() == call_depth =>1288            {1289                let guar = err.emit();1290                // Preserve the call expression so later passes can still diagnose the callee,1291                // while treating the malformed `&raw <expr>` argument as an error expression.1292                let args = self.recover_raw_ref_call_args(guar);1293                return self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args));1294            }1295            Err(err) => Err(err),1296        };1297        match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) {1298            Ok(expr) => expr,1299            Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err),1300        }1301    }13021303    fn recover_raw_ref_call_args(&mut self, guar: ErrorGuaranteed) -> ThinVec<Box<Expr>> {1304        let err_span = self.prev_token.span.to(self.token.span);1305        let mut args = thin_vec![self.mk_expr_err(err_span, guar)];1306        while !self.token.kind.is_close_delim_or_eof() {1307            if self.eat(exp!(Comma)) && !self.token.kind.is_close_delim_or_eof() {1308                args.push(self.mk_expr_err(self.prev_token.span.shrink_to_hi(), guar));1309            } else {1310                self.parse_token_tree();1311            }1312        }1313        let _ = self.eat(exp!(CloseParen));1314        args1315    }13161317    /// If we encounter a parser state that looks like the user has written a `struct` literal with1318    /// parentheses instead of braces, recover the parser state and provide suggestions.1319    #[instrument(skip(self, seq, snapshot), level = "trace")]1320    fn maybe_recover_struct_lit_bad_delims(1321        &mut self,1322        lo: Span,1323        open_paren: Span,1324        seq: PResult<'a, Box<Expr>>,1325        snapshot: Option<(SnapshotParser<'a>, ExprKind)>,1326    ) -> PResult<'a, Box<Expr>> {1327        match (self.may_recover(), seq, snapshot) {1328            (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {1329                snapshot.bump(); // `(`1330                match snapshot.parse_struct_fields(path.clone(), false, exp!(CloseParen)) {1331                    Ok((fields, ..)) if snapshot.eat(exp!(CloseParen)) => {1332                        // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest1333                        // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.1334                        self.restore_snapshot(snapshot);1335                        let close_paren = self.prev_token.span;1336                        let span = lo.to(close_paren);1337                        // filter shorthand fields1338                        let fields: Vec<_> =1339                            fields.into_iter().filter(|field| !field.is_shorthand).collect();13401341                        let guar = if !fields.is_empty() &&1342                            // `token.kind` should not be compared here.1343                            // This is because the `snapshot.token.kind` is treated as the same as1344                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even1345                            // if they are different.1346                            self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")1347                        {1348                            err.cancel();1349                            let type_str = pprust::path_to_string(&path);1350                            self.dcx()1351                                .create_err(errors::ParenthesesWithStructFields {1352                                    span,1353                                    braces_for_struct: errors::BracesForStructLiteral {1354                                        first: open_paren,1355                                        second: close_paren,1356                                        r#type: type_str.clone(),1357                                    },1358                                    no_fields_for_fn: errors::NoFieldsForFnCall {1359                                        r#type: type_str,1360                                        fields: fields1361                                            .into_iter()1362                                            .map(|field| field.span.until(field.expr.span))1363                                            .collect(),1364                                    },1365                                })1366                                .emit()1367                        } else {1368                            err.emit()1369                        };1370                        Ok(self.mk_expr_err(span, guar))1371                    }1372                    Ok(_) => Err(err),1373                    Err(err2) => {1374                        err2.cancel();1375                        Err(err)1376                    }1377                }1378            }1379            (_, seq, _) => seq,1380        }1381    }13821383    /// Parse an indexing expression `expr[...]`.1384    fn parse_expr_index(&mut self, lo: Span, base: Box<Expr>) -> PResult<'a, Box<Expr>> {1385        let prev_span = self.prev_token.span;1386        let open_delim_span = self.token.span;1387        self.bump(); // `[`1388        let index = self.parse_expr()?;1389        self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?;1390        self.expect(exp!(CloseBracket))?;1391        Ok(self.mk_expr(1392            lo.to(self.prev_token.span),1393            self.mk_index(base, index, open_delim_span.to(self.prev_token.span)),1394        ))1395    }13961397    /// Assuming we have just parsed `.`, continue parsing into an expression.1398    fn parse_dot_suffix(&mut self, self_arg: Box<Expr>, lo: Span) -> PResult<'a, Box<Expr>> {1399        if self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) {1400            return Ok(self.mk_await_expr(self_arg, lo));1401        }14021403        if self.eat_keyword(exp!(Use)) {1404            let use_span = self.prev_token.span;1405            self.psess.gated_spans.gate(sym::ergonomic_clones, use_span);1406            return Ok(self.mk_use_expr(self_arg, lo));1407        }14081409        // Post-fix match1410        if self.eat_keyword(exp!(Match)) {1411            let match_span = self.prev_token.span;1412            self.psess.gated_spans.gate(sym::postfix_match, match_span);1413            return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);1414        }14151416        // Parse a postfix `yield`.1417        if self.eat_keyword(exp!(Yield)) {1418            let yield_span = self.prev_token.span;1419            self.psess.gated_spans.gate(sym::yield_expr, yield_span);1420            return Ok(1421                self.mk_expr(lo.to(yield_span), ExprKind::Yield(YieldKind::Postfix(self_arg)))1422            );1423        }14241425        let fn_span_lo = self.token.span;1426        let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;1427        self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]);1428        self.check_turbofish_missing_angle_brackets(&mut seg);14291430        if self.check(exp!(OpenParen)) {1431            // Method call `expr.f()`1432            let args = self.parse_expr_paren_seq()?;1433            let fn_span = fn_span_lo.to(self.prev_token.span);1434            let span = lo.to(self.prev_token.span);1435            Ok(self.mk_expr(1436                span,1437                ExprKind::MethodCall(Box::new(ast::MethodCall {1438                    seg,1439                    receiver: self_arg,1440                    args,1441                    span: fn_span,1442                })),1443            ))1444        } else {1445            // Field access `expr.f`1446            let span = lo.to(self.prev_token.span);1447            if let Some(args) = seg.args {1448                // See `StashKey::GenericInFieldExpr` for more info on why we stash this.1449                self.dcx()1450                    .create_err(errors::FieldExpressionWithGeneric(args.span()))1451                    .stash(seg.ident.span, StashKey::GenericInFieldExpr);1452            }14531454            Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident)))1455        }1456    }14571458    /// At the bottom (top?) of the precedence hierarchy,1459    /// Parses things like parenthesized exprs, macros, `return`, etc.1460    ///1461    /// N.B., this does not parse outer attributes, and is private because it only works1462    /// correctly if called from `parse_expr_dot_or_call`.1463    fn parse_expr_bottom(&mut self) -> PResult<'a, Box<Expr>> {1464        maybe_recover_from_interpolated_ty_qpath!(self, true);14651466        let span = self.token.span;1467        if let Some(expr) = self.eat_metavar_seq_with_matcher(1468            |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),1469            |this| {1470                // Force collection (as opposed to just `parse_expr`) is required to avoid the1471                // attribute duplication seen in #138478.1472                let expr = this.parse_expr_force_collect();1473                // FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly1474                // related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line1475                // `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in1476                // `compiler/rustc_index/src/bit_set/tests.rs`.1477                if this.token.kind == token::Comma {1478                    this.bump();1479                }1480                expr1481            },1482        ) {1483            return Ok(expr);1484        } else if let Some(lit) =1485            self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())1486        {1487            return Ok(lit);1488        } else if let Some(block) =1489            self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block())1490        {1491            return Ok(self.mk_expr(span, ExprKind::Block(block, None)));1492        } else if let Some(path) =1493            self.eat_metavar_seq(MetaVarKind::Path, |this| this.parse_path(PathStyle::Type))1494        {1495            return Ok(self.mk_expr(span, ExprKind::Path(None, path)));1496        }14971498        // Outer attributes are already parsed and will be1499        // added to the return value after the fact.15001501        let restrictions = self.restrictions;1502        self.with_res(restrictions - Restrictions::ALLOW_LET, |this| {1503            // Note: adding new syntax here? Don't forget to adjust `TokenKind::can_begin_expr()`.1504            let lo = this.token.span;1505            if let token::Literal(_) = this.token.kind {1506                // This match arm is a special-case of the `_` match arm below and1507                // could be removed without changing functionality, but it's faster1508                // to have it here, especially for programs with large constants.1509                this.parse_expr_lit()1510            } else if this.check(exp!(OpenParen)) {1511                this.parse_expr_tuple_parens(restrictions)1512            } else if this.check(exp!(OpenBrace)) {1513                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {1514                    return Ok(expr);1515                }1516                this.parse_expr_block(None, lo, BlockCheckMode::Default)1517            } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {1518                this.parse_expr_closure().map_err(|mut err| {1519                    // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`1520                    // then suggest parens around the lhs.1521                    if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {1522                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));1523                    }1524                    err1525                })1526            } else if this.check(exp!(OpenBracket)) {1527                this.parse_expr_array_or_repeat(exp!(CloseBracket))1528            } else if this.is_builtin() {1529                this.parse_expr_builtin()1530            } else if this.check_path() {1531                this.parse_expr_path_start()1532            } else if this.check_keyword(exp!(Move))1533                || this.check_keyword(exp!(Use))1534                || this.check_keyword(exp!(Static))1535                || this.check_const_closure()1536            {1537                this.parse_expr_closure()1538            } else if this.eat_keyword(exp!(If)) {1539                this.parse_expr_if()1540            } else if this.check_keyword(exp!(For)) {1541                if this.choose_generics_over_qpath(1) {1542                    this.parse_expr_closure()1543                } else {1544                    assert!(this.eat_keyword(exp!(For)));1545                    this.parse_expr_for(None, lo)1546                }1547            } else if this.eat_keyword(exp!(While)) {1548                this.parse_expr_while(None, lo)1549            } else if let Some(label) = this.eat_label() {1550                this.parse_expr_labeled(label, true)1551            } else if this.eat_keyword(exp!(Loop)) {1552                this.parse_expr_loop(None, lo).map_err(|mut err| {1553                    err.span_label(lo, "while parsing this `loop` expression");1554                    err1555                })1556            } else if this.eat_keyword(exp!(Match)) {1557                this.parse_expr_match().map_err(|mut err| {1558                    err.span_label(lo, "while parsing this `match` expression");1559                    err1560                })1561            } else if this.eat_keyword(exp!(Unsafe)) {1562                this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(1563                    |mut err| {1564                        err.span_label(lo, "while parsing this `unsafe` expression");1565                        err1566                    },1567                )1568            } else if this.check_inline_const(0) {1569                this.parse_const_block(lo, false)1570            } else if this.may_recover() && this.is_do_catch_block() {1571                this.recover_do_catch()1572            } else if this.is_try_block() {1573                this.expect_keyword(exp!(Try))?;1574                this.parse_try_block(lo)1575            } else if this.eat_keyword(exp!(Return)) {1576                this.parse_expr_return()1577            } else if this.eat_keyword(exp!(Continue)) {1578                this.parse_expr_continue(lo)1579            } else if this.eat_keyword(exp!(Break)) {1580                this.parse_expr_break()1581            } else if this.eat_keyword(exp!(Yield)) {1582                this.parse_expr_yield()1583            } else if this.is_do_yeet() {1584                this.parse_expr_yeet()1585            } else if this.eat_keyword(exp!(Become)) {1586                this.parse_expr_become()1587            } else if this.check_keyword(exp!(Let)) {1588                this.parse_expr_let(restrictions)1589            } else if this.eat_keyword(exp!(Underscore)) {1590                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(true)? {1591                    return Ok(expr);1592                }1593                Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))1594            } else if this.token_uninterpolated_span().at_least_rust_2018() {1595                // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.1596                let at_async = this.check_keyword(exp!(Async));1597                // check for `gen {}` and `gen move {}`1598                // or `async gen {}` and `async gen move {}`1599                // FIXME: (async) gen closures aren't yet parsed.1600                // FIXME(gen_blocks): Parse `gen async` and suggest swap1601                if this.token_uninterpolated_span().at_least_rust_2024()1602                    && this.is_gen_block(kw::Gen, at_async as usize)1603                {1604                    this.parse_gen_block()1605                // Check for `async {` and `async move {`,1606                } else if this.is_gen_block(kw::Async, 0) {1607                    this.parse_gen_block()1608                } else if at_async {1609                    this.parse_expr_closure()1610                } else if this.eat_keyword_noexpect(kw::Await) {1611                    this.recover_incorrect_await_syntax(lo)1612                } else {1613                    this.parse_expr_lit()1614                }1615            } else {1616                this.parse_expr_lit()1617            }1618        })1619    }16201621    fn parse_expr_lit(&mut self) -> PResult<'a, Box<Expr>> {1622        let lo = self.token.span;1623        match self.parse_opt_token_lit() {1624            Some((token_lit, _)) => {1625                let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit));1626                self.maybe_recover_from_bad_qpath(expr)1627            }1628            None => self.try_macro_suggestion(),1629        }1630    }16311632    fn parse_expr_tuple_parens(&mut self, restrictions: Restrictions) -> PResult<'a, Box<Expr>> {1633        let lo = self.token.span;1634        self.expect(exp!(OpenParen))?;1635        let (es, trailing_comma) = match self.parse_seq_to_end(1636            exp!(CloseParen),1637            SeqSep::trailing_allowed(exp!(Comma)),1638            |p| p.parse_expr_catch_underscore(restrictions.intersection(Restrictions::ALLOW_LET)),1639        ) {1640            Ok(x) => x,1641            Err(err) => {1642                return Ok(self.recover_seq_parse_error(1643                    exp!(OpenParen),1644                    exp!(CloseParen),1645                    lo,1646                    err,1647                ));1648            }1649        };1650        let kind = if es.len() == 1 && matches!(trailing_comma, Trailing::No) {1651            // `(e)` is parenthesized `e`.1652            ExprKind::Paren(es.into_iter().next().unwrap())1653        } else {1654            // `(e,)` is a tuple with only one field, `e`.1655            ExprKind::Tup(es)1656        };1657        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1658        self.maybe_recover_from_bad_qpath(expr)1659    }16601661    fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair) -> PResult<'a, Box<Expr>> {1662        let lo = self.token.span;1663        self.bump(); // `[` or other open delim16641665        let kind = if self.eat(close) {1666            // Empty vector1667            ExprKind::Array(ThinVec::new())1668        } else {1669            // Non-empty vector1670            let first_expr = self.parse_expr()?;1671            if self.eat(exp!(Semi)) {1672                // Repeating array syntax: `[ 0; 512 ]`1673                let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?;1674                self.expect(close)?;1675                ExprKind::Repeat(first_expr, count)1676            } else if self.eat(exp!(Comma)) {1677                // Vector with two or more elements.1678                let sep = SeqSep::trailing_allowed(exp!(Comma));1679                let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;1680                exprs.insert(0, first_expr);1681                ExprKind::Array(exprs)1682            } else {1683                // Vector with one element1684                self.expect(close)?;1685                ExprKind::Array(thin_vec![first_expr])1686            }1687        };1688        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1689        self.maybe_recover_from_bad_qpath(expr)1690    }16911692    fn parse_expr_path_start(&mut self) -> PResult<'a, Box<Expr>> {1693        let maybe_eq_tok = self.prev_token;1694        let (qself, path) = if self.eat_lt() {1695            let lt_span = self.prev_token.span;1696            let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {1697                // Suggests using '<=' if there is an error parsing qpath when the previous token1698                // is an '=' token. Only emits suggestion if the '<' token and '=' token are1699                // directly adjacent (i.e. '=<')1700                if maybe_eq_tok == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {1701                    let eq_lt = maybe_eq_tok.span.to(lt_span);1702                    err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);1703                }1704                err1705            })?;1706            (Some(qself), path)1707        } else {1708            (None, self.parse_path(PathStyle::Expr)?)1709        };17101711        // `!`, as an operator, is prefix, so we know this isn't that.1712        let (span, kind) = if self.eat(exp!(Bang)) {1713            // MACRO INVOCATION expression1714            if qself.is_some() {1715                self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span));1716            }1717            let lo = path.span;1718            let mac = Box::new(MacCall { path, args: self.parse_delim_args()? });1719            (lo.to(self.prev_token.span), ExprKind::MacCall(mac))1720        } else if self.check(exp!(OpenBrace))1721            && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)1722        {1723            if qself.is_some() {1724                self.psess.gated_spans.gate(sym::more_qualified_paths, path.span);1725            }1726            return expr;1727        } else {1728            (path.span, ExprKind::Path(qself, path))1729        };17301731        let expr = self.mk_expr(span, kind);1732        self.maybe_recover_from_bad_qpath(expr)1733    }17341735    /// Parse `'label: $expr`. The label is already parsed.1736    pub(super) fn parse_expr_labeled(1737        &mut self,1738        label_: Label,1739        mut consume_colon: bool,1740    ) -> PResult<'a, Box<Expr>> {1741        let lo = label_.ident.span;1742        let label = Some(label_);1743        let ate_colon = self.eat(exp!(Colon));1744        let tok_sp = self.token.span;1745        let expr = if self.eat_keyword(exp!(While)) {1746            self.parse_expr_while(label, lo)1747        } else if self.eat_keyword(exp!(For)) {1748            self.parse_expr_for(label, lo)1749        } else if self.eat_keyword(exp!(Loop)) {1750            self.parse_expr_loop(label, lo)1751        } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() {1752            self.parse_expr_block(label, lo, BlockCheckMode::Default)1753        } else if !ate_colon1754            && self.may_recover()1755            && (self.token.kind.close_delim().is_some() || self.token.is_punct())1756            && could_be_unclosed_char_literal(label_.ident)1757        {1758            let (lit, _) =1759                self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {1760                    self_.dcx().create_err(errors::UnexpectedTokenAfterLabel {1761                        span: self_.token.span,1762                        remove_label: None,1763                        enclose_in_block: None,1764                    })1765                });1766            consume_colon = false;1767            Ok(self.mk_expr(lo, ExprKind::Lit(lit)))1768        } else if !ate_colon1769            && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))1770        {1771            // We're probably inside of a `Path<'a>` that needs a turbofish1772            let guar = self.dcx().emit_err(errors::UnexpectedTokenAfterLabel {1773                span: self.token.span,1774                remove_label: None,1775                enclose_in_block: None,1776            });1777            consume_colon = false;1778            Ok(self.mk_expr_err(lo, guar))1779        } else {1780            let mut err = errors::UnexpectedTokenAfterLabel {1781                span: self.token.span,1782                remove_label: None,1783                enclose_in_block: None,1784            };17851786            // Continue as an expression in an effort to recover on `'label: non_block_expr`.1787            let expr = self.parse_expr().map(|expr| {1788                let span = expr.span;17891790                let found_labeled_breaks = {1791                    struct FindLabeledBreaksVisitor;17921793                    impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {1794                        type Result = ControlFlow<()>;1795                        fn visit_expr(&mut self, ex: &'ast Expr) -> ControlFlow<()> {1796                            if let ExprKind::Break(Some(_label), _) = ex.kind {1797                                ControlFlow::Break(())1798                            } else {1799                                walk_expr(self, ex)1800                            }1801                        }1802                    }18031804                    FindLabeledBreaksVisitor.visit_expr(&expr).is_break()1805                };18061807                // Suggestion involves adding a labeled block.1808                //1809                // If there are no breaks that may use this label, suggest removing the label and1810                // recover to the unmodified expression.1811                if !found_labeled_breaks {1812                    err.remove_label = Some(lo.until(span));18131814                    return expr;1815                }18161817                err.enclose_in_block = Some(errors::UnexpectedTokenAfterLabelSugg {1818                    left: span.shrink_to_lo(),1819                    right: span.shrink_to_hi(),1820                });18211822                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.1823                let stmt = self.mk_stmt(span, StmtKind::Expr(expr));1824                let blk = self.mk_block(thin_vec![stmt], BlockCheckMode::Default, span);1825                self.mk_expr(span, ExprKind::Block(blk, label))1826            });18271828            self.dcx().emit_err(err);1829            expr1830        }?;18311832        if !ate_colon && consume_colon {1833            self.dcx().emit_err(errors::RequireColonAfterLabeledExpression {1834                span: expr.span,1835                label: lo,1836                label_end: lo.between(tok_sp),1837            });1838        }18391840        Ok(expr)1841    }18421843    /// Emit an error when a char is parsed as a lifetime or label because of a missing quote.1844    pub(super) fn recover_unclosed_char<L>(1845        &self,1846        ident: Ident,1847        mk_lit_char: impl FnOnce(Symbol, Span) -> L,1848        err: impl FnOnce(&Self) -> Diag<'a>,1849    ) -> L {1850        assert!(could_be_unclosed_char_literal(ident));1851        self.dcx()1852            .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| {1853                err.span_suggestion_verbose(1854                    ident.span.shrink_to_hi(),1855                    "add `'` to close the char literal",1856                    "'",1857                    Applicability::MaybeIncorrect,1858                );1859            })1860            .unwrap_or_else(|| {1861                err(self)1862                    .with_span_suggestion_verbose(1863                        ident.span.shrink_to_hi(),1864                        "add `'` to close the char literal",1865                        "'",1866                        Applicability::MaybeIncorrect,1867                    )1868                    .emit()1869            });1870        let name = ident.without_first_quote().name;1871        mk_lit_char(name, ident.span)1872    }18731874    /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.1875    fn recover_do_catch(&mut self) -> PResult<'a, Box<Expr>> {1876        let lo = self.token.span;18771878        self.bump(); // `do`1879        self.bump(); // `catch`18801881        let span = lo.to(self.prev_token.span);1882        self.dcx().emit_err(errors::DoCatchSyntaxRemoved { span });18831884        self.parse_try_block(lo)1885    }18861887    /// Parse an expression if the token can begin one.1888    fn parse_expr_opt(&mut self) -> PResult<'a, Option<Box<Expr>>> {1889        Ok(if self.token.can_begin_expr() { Some(self.parse_expr()?) } else { None })1890    }18911892    /// Parse `"return" expr?`.1893    fn parse_expr_return(&mut self) -> PResult<'a, Box<Expr>> {1894        let lo = self.prev_token.span;1895        let kind = ExprKind::Ret(self.parse_expr_opt()?);1896        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1897        self.maybe_recover_from_bad_qpath(expr)1898    }18991900    /// Parse `"do" "yeet" expr?`.1901    fn parse_expr_yeet(&mut self) -> PResult<'a, Box<Expr>> {1902        let lo = self.token.span;19031904        self.bump(); // `do`1905        self.bump(); // `yeet`19061907        let kind = ExprKind::Yeet(self.parse_expr_opt()?);19081909        let span = lo.to(self.prev_token.span);1910        self.psess.gated_spans.gate(sym::yeet_expr, span);1911        let expr = self.mk_expr(span, kind);1912        self.maybe_recover_from_bad_qpath(expr)1913    }19141915    /// Parse `"become" expr`, with `"become"` token already eaten.1916    fn parse_expr_become(&mut self) -> PResult<'a, Box<Expr>> {1917        let lo = self.prev_token.span;1918        let kind = ExprKind::Become(self.parse_expr()?);1919        let span = lo.to(self.prev_token.span);1920        self.psess.gated_spans.gate(sym::explicit_tail_calls, span);1921        let expr = self.mk_expr(span, kind);1922        self.maybe_recover_from_bad_qpath(expr)1923    }19241925    /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.1926    /// If the label is followed immediately by a `:` token, the label and `:` are1927    /// parsed as part of the expression (i.e. a labeled loop). The language team has1928    /// decided in #87026 to require parentheses as a visual aid to avoid confusion if1929    /// the break expression of an unlabeled break is a labeled loop (as in1930    /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value1931    /// expression only gets a warning for compatibility reasons; and a labeled break1932    /// with a labeled loop does not even get a warning because there is no ambiguity.1933    fn parse_expr_break(&mut self) -> PResult<'a, Box<Expr>> {1934        let lo = self.prev_token.span;1935        let mut label = self.eat_label();1936        let kind = if self.token == token::Colon1937            && let Some(label) = label.take()1938        {1939            // The value expression can be a labeled loop, see issue #86948, e.g.:1940            // `loop { break 'label: loop { break 'label 42; }; }`1941            let lexpr = self.parse_expr_labeled(label, true)?;1942            self.dcx().emit_err(errors::LabeledLoopInBreak {1943                span: lexpr.span,1944                sub: errors::WrapInParentheses::Expression {1945                    left: lexpr.span.shrink_to_lo(),1946                    right: lexpr.span.shrink_to_hi(),1947                },1948            });1949            Some(lexpr)1950        } else if self.token != token::OpenBrace1951            || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)1952        {1953            let mut expr = self.parse_expr_opt()?;1954            if let Some(expr) = &mut expr {1955                if label.is_some()1956                    && match &expr.kind {1957                        ExprKind::While(_, _, None)1958                        | ExprKind::ForLoop { label: None, .. }1959                        | ExprKind::Loop(_, None, _) => true,1960                        ExprKind::Block(block, None) => {1961                            matches!(block.rules, BlockCheckMode::Default)1962                        }1963                        _ => false,1964                    }1965                {1966                    let span = expr.span;1967                    self.psess.buffer_lint(1968                        BREAK_WITH_LABEL_AND_LOOP,1969                        lo.to(expr.span),1970                        ast::CRATE_NODE_ID,1971                        errors::BreakWithLabelAndLoop {1972                            sub: errors::BreakWithLabelAndLoopSub {1973                                left: span.shrink_to_lo(),1974                                right: span.shrink_to_hi(),1975                            },1976                        },1977                    );1978                }19791980                // Recover `break label aaaaa`1981                if self.may_recover()1982                    && let ExprKind::Path(None, p) = &expr.kind1983                    && let [segment] = &*p.segments1984                    && let &ast::PathSegment { ident, args: None, .. } = segment1985                    && let Some(next) = self.parse_expr_opt()?1986                {1987                    label = Some(self.recover_ident_into_label(ident));1988                    *expr = next;1989                }1990            }19911992            expr1993        } else {1994            None1995        };1996        let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind));1997        self.maybe_recover_from_bad_qpath(expr)1998    }19992000    /// Parse `"continue" label?`.

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.