compiler/rustc_parse/src/parser/expr.rs RUST 4,451 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 4,451.
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(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {568                make_it!(this, attrs, |this, _| this.recover_not_expr(lo))569            }570            _ => return this.parse_expr_dot_or_call(attrs),571        }572    }573574    fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, Box<Expr>)> {575        self.bump();576        let attrs = self.parse_outer_attributes()?;577        let expr = if self.token.is_range_separator() {578            self.parse_expr_prefix_range(attrs)579        } else {580            self.parse_expr_prefix(attrs)581        }?;582        let span = self.interpolated_or_expr_span(&expr);583        Ok((lo.to(span), expr))584    }585586    fn parse_expr_unary(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> {587        let (span, expr) = self.parse_expr_prefix_common(lo)?;588        Ok((span, self.mk_unary(op, expr)))589    }590591    /// Recover on `~expr` in favor of `!expr`.592    fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {593        self.dcx().emit_err(errors::TildeAsUnaryOperator(lo));594595        self.parse_expr_unary(lo, UnOp::Not)596    }597598    /// Parse `box expr` - this syntax has been removed, but we still parse this599    /// for now to provide a more useful error600    fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {601        let (span, expr) = self.parse_expr_prefix_common(box_kw)?;602        // Make a multipart suggestion instead of `span_to_snippet` in case source isn't available603        let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));604        let hi = span.shrink_to_hi();605        let sugg = errors::AddBoxNew { box_kw_and_lo, hi };606        let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span, sugg });607        Ok((span, ExprKind::Err(guar)))608    }609610    fn is_mistaken_not_ident_negation(&self) -> bool {611        let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {612            // These tokens can start an expression after `!`, but613            // can't continue an expression after an ident614            token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw),615            token::Literal(..) | token::Pound => true,616            _ => t.is_metavar_expr(),617        };618        self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr)619    }620621    /// Recover on `not expr` in favor of `!expr`.622    fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {623        let negated_token = self.look_ahead(1, |t| *t);624625        let sub_diag = if negated_token.is_numeric_lit() {626            errors::NotAsNegationOperatorSub::SuggestNotBitwise627        } else if negated_token.is_bool_lit() {628            errors::NotAsNegationOperatorSub::SuggestNotLogical629        } else {630            errors::NotAsNegationOperatorSub::SuggestNotDefault631        };632633        self.dcx().emit_err(errors::NotAsNegationOperator {634            negated: negated_token.span,635            negated_desc: super::token_descr(&negated_token),636            // Span the `not` plus trailing whitespace to avoid637            // trailing whitespace after the `!` in our suggestion638            sub: sub_diag(639                self.psess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),640            ),641        });642643        self.parse_expr_unary(lo, UnOp::Not)644    }645646    /// Returns the span of expr if it was not interpolated, or the span of the interpolated token.647    fn interpolated_or_expr_span(&self, expr: &Expr) -> Span {648        match self.prev_token.kind {649            token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span,650            token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => {651                // `expr.span` is the interpolated span, because invisible open652                // and close delims both get marked with the same span, one653                // that covers the entire thing between them. (See654                // `rustc_expand::mbe::transcribe::transcribe`.)655                self.prev_token.span656            }657            _ => expr.span,658        }659    }660661    fn parse_assoc_op_cast(662        &mut self,663        lhs: Box<Expr>,664        lhs_span: Span,665        op_span: Span,666        expr_kind: fn(Box<Expr>, Box<Ty>) -> ExprKind,667    ) -> PResult<'a, Box<Expr>> {668        let mk_expr = |this: &mut Self, lhs: Box<Expr>, rhs: Box<Ty>| {669            this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs))670        };671672        // Save the state of the parser before parsing type normally, in case there is a673        // LessThan comparison after this cast.674        let parser_snapshot_before_type = self.clone();675        let cast_expr = match self.parse_as_cast_ty() {676            Ok(rhs) => mk_expr(self, lhs, rhs),677            Err(type_err) => {678                if !self.may_recover() {679                    return Err(type_err);680                }681682                // Rewind to before attempting to parse the type with generics, to recover683                // from situations like `x as usize < y` in which we first tried to parse684                // `usize < y` as a type with generic arguments.685                let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type);686687                // Check for typo of `'a: loop { break 'a }` with a missing `'`.688                match (&lhs.kind, &self.token.kind) {689                    (690                        // `foo: `691                        ExprKind::Path(None, ast::Path { segments, .. }),692                        token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No),693                    ) if let [segment] = segments.as_slice() => {694                        let snapshot = self.create_snapshot_for_diagnostic();695                        let label = Label {696                            ident: Ident::from_str_and_span(697                                &format!("'{}", segment.ident),698                                segment.ident.span,699                            ),700                        };701                        match self.parse_expr_labeled(label, false) {702                            Ok(expr) => {703                                type_err.cancel();704                                self.dcx().emit_err(errors::MalformedLoopLabel {705                                    span: label.ident.span,706                                    suggestion: label.ident.span.shrink_to_lo(),707                                });708                                return Ok(expr);709                            }710                            Err(err) => {711                                err.cancel();712                                self.restore_snapshot(snapshot);713                            }714                        }715                    }716                    _ => {}717                }718719                match self.parse_path(PathStyle::Expr) {720                    Ok(path) => {721                        let span_after_type = parser_snapshot_after_type.token.span;722                        let expr = mk_expr(723                            self,724                            lhs,725                            self.mk_ty(path.span, TyKind::Path(None, path.clone())),726                        );727728                        let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);729                        match self.token.kind {730                            token::Lt => {731                                self.dcx().emit_err(errors::ComparisonInterpretedAsGeneric {732                                    comparison: self.token.span,733                                    r#type: pprust::path_to_string(&path),734                                    args: args_span,735                                    suggestion: errors::ComparisonInterpretedAsGenericSugg {736                                        left: expr.span.shrink_to_lo(),737                                        right: expr.span.shrink_to_hi(),738                                    },739                                })740                            }741                            token::Shl => self.dcx().emit_err(errors::ShiftInterpretedAsGeneric {742                                shift: self.token.span,743                                r#type: pprust::path_to_string(&path),744                                args: args_span,745                                suggestion: errors::ShiftInterpretedAsGenericSugg {746                                    left: expr.span.shrink_to_lo(),747                                    right: expr.span.shrink_to_hi(),748                                },749                            }),750                            _ => {751                                // We can end up here even without `<` being the next token, for752                                // example because `parse_ty_no_plus` returns `Err` on keywords,753                                // but `parse_path` returns `Ok` on them due to error recovery.754                                // Return original error and parser state.755                                *self = parser_snapshot_after_type;756                                return Err(type_err);757                            }758                        };759760                        // Successfully parsed the type path leaving a `<` yet to parse.761                        type_err.cancel();762763                        // Keep `x as usize` as an expression in AST and continue parsing.764                        expr765                    }766                    Err(path_err) => {767                        // Couldn't parse as a path, return original error and parser state.768                        path_err.cancel();769                        *self = parser_snapshot_after_type;770                        return Err(type_err);771                    }772                }773            }774        };775776        // Try to parse a postfix operator such as `.`, `?`, or index (`[]`)777        // after a cast. If one is present, emit an error then return a valid778        // parse tree; For something like `&x as T[0]` will be as if it was779        // written `((&x) as T)[0]`.780781        let span = cast_expr.span;782783        let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?;784785        // Check if an illegal postfix operator has been added after the cast.786        // If the resulting expression is not a cast, it is an illegal postfix operator.787        if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {788            let msg = format!(789                "cast cannot be followed by {}",790                match with_postfix.kind {791                    ExprKind::Index(..) => "indexing",792                    ExprKind::Try(_) => "`?`",793                    ExprKind::Field(_, _) => "a field access",794                    ExprKind::MethodCall(_) => "a method call",795                    ExprKind::Call(_, _) => "a function call",796                    ExprKind::Await(_, _) => "`.await`",797                    ExprKind::Use(_, _) => "`.use`",798                    ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`",799                    ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",800                    ExprKind::Err(_) => return Ok(with_postfix),801                    _ => unreachable!(802                        "did not expect {:?} as an illegal postfix operator following cast",803                        with_postfix.kind804                    ),805                }806            );807            let mut err = self.dcx().struct_span_err(span, msg);808809            let suggest_parens = |err: &mut Diag<'_>| {810                let suggestions = vec![811                    (span.shrink_to_lo(), "(".to_string()),812                    (span.shrink_to_hi(), ")".to_string()),813                ];814                err.multipart_suggestion(815                    "try surrounding the expression in parentheses",816                    suggestions,817                    Applicability::MachineApplicable,818                );819            };820821            suggest_parens(&mut err);822823            err.emit();824        };825        Ok(with_postfix)826    }827828    /// Parse `& mut? <expr>` or `& raw [ const | mut ] <expr>`.829    fn parse_expr_borrow(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {830        self.expect_and()?;831        let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);832        let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.833        let (borrow_kind, mutbl) = self.parse_borrow_modifiers();834        let attrs = self.parse_outer_attributes()?;835        let expr = if self.token.is_range_separator() {836            self.parse_expr_prefix_range(attrs)837        } else {838            self.parse_expr_prefix(attrs)839        }?;840        let hi = self.interpolated_or_expr_span(&expr);841        let span = lo.to(hi);842        if let Some(lt) = lifetime {843            self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span));844        }845846        // Add expected tokens if we parsed `&raw` as an expression.847        // This will make sure we see "expected `const`, `mut`", and848        // guides recovery in case we write `&raw expr`.849        if borrow_kind == ast::BorrowKind::Ref850            && mutbl == ast::Mutability::Not851            && matches!(&expr.kind, ExprKind::Path(None, p) if *p == kw::Raw)852        {853            self.expected_token_types.insert(TokenType::KwMut);854            self.expected_token_types.insert(TokenType::KwConst);855        }856857        Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr)))858    }859860    fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {861        self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });862    }863864    /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.865    fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {866        if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {867            // `raw [ const | mut ]`.868            let found_raw = self.eat_keyword(exp!(Raw));869            assert!(found_raw);870            let mutability = self.parse_mut_or_const().unwrap();871            (ast::BorrowKind::Raw, mutability)872        } else {873            match self.parse_pin_and_mut() {874                // `mut?`875                (ast::Pinnedness::Not, mutbl) => (ast::BorrowKind::Ref, mutbl),876                // `pin [ const | mut ]`.877                // `pin` has been gated in `self.parse_pin_and_mut()` so we don't878                // need to gate it here.879                (ast::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl),880            }881        }882    }883884    /// Parses `a.b` or `a(13)` or `a[4]` or just `a`.885    fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, Box<Expr>> {886        self.collect_tokens_for_expr(attrs, |this, attrs| {887            let base = this.parse_expr_bottom()?;888            let span = this.interpolated_or_expr_span(&base);889            this.parse_expr_dot_or_call_with(attrs, base, span)890        })891    }892893    pub(super) fn parse_expr_dot_or_call_with(894        &mut self,895        mut attrs: ast::AttrVec,896        mut e: Box<Expr>,897        lo: Span,898    ) -> PResult<'a, Box<Expr>> {899        let mut res = ensure_sufficient_stack(|| {900            loop {901                let has_question =902                    if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {903                        // We are using noexpect here because we don't expect a `?` directly after904                        // a `return` which could be suggested otherwise.905                        self.eat_noexpect(&token::Question)906                    } else {907                        self.eat(exp!(Question))908                    };909                if has_question {910                    // `expr?`911                    e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e));912                    continue;913                }914                let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {915                    // We are using noexpect here because we don't expect a `.` directly after916                    // a `return` which could be suggested otherwise.917                    self.eat_noexpect(&token::Dot)918                } else if self.token == TokenKind::RArrow && self.may_recover() {919                    // Recovery for `expr->suffix`.920                    self.bump();921                    let span = self.prev_token.span;922                    self.dcx().emit_err(errors::ExprRArrowCall { span });923                    true924                } else {925                    self.eat(exp!(Dot))926                };927                if has_dot {928                    // expr.f929                    e = self.parse_dot_suffix_expr(lo, e)?;930                    continue;931                }932                if self.expr_is_complete(&e) {933                    return Ok(e);934                }935                e = match self.token.kind {936                    token::OpenParen => self.parse_expr_fn_call(lo, e),937                    token::OpenBracket => self.parse_expr_index(lo, e)?,938                    _ => return Ok(e),939                }940            }941        });942943        // Stitch the list of outer attributes onto the return value. A little944        // bit ugly, but the best way given the current code structure.945        if !attrs.is_empty()946            && let Ok(expr) = &mut res947        {948            mem::swap(&mut expr.attrs, &mut attrs);949            expr.attrs.extend(attrs)950        }951        res952    }953954    pub(super) fn parse_dot_suffix_expr(955        &mut self,956        lo: Span,957        base: Box<Expr>,958    ) -> PResult<'a, Box<Expr>> {959        // At this point we've consumed something like `expr.` and `self.token` holds the token960        // after the dot.961        match self.token.uninterpolate().kind {962            token::Ident(..) => self.parse_dot_suffix(base, lo),963            token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {964                let ident_span = self.token.span;965                self.bump();966                Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix))967            }968            token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => {969                Ok(match self.break_up_float(symbol, self.token.span) {970                    // 1e2971                    DestructuredFloat::Single(sym, _sp) => {972                        // `foo.1e2`: a single complete dot access, fully consumed. We end up with973                        // the `1e2` token in `self.prev_token` and the following token in974                        // `self.token`.975                        let ident_span = self.token.span;976                        self.bump();977                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix)978                    }979                    // 1.980                    DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {981                        // `foo.1.`: a single complete dot access and the start of another.982                        // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in983                        // `self.token`.984                        assert!(suffix.is_none());985                        self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span);986                        self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));987                        self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None)988                    }989                    // 1.2 | 1.2e3990                    DestructuredFloat::MiddleDot(991                        sym1,992                        ident1_span,993                        _dot_span,994                        sym2,995                        ident2_span,996                    ) => {997                        // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with998                        // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following999                        // token in `self.token`.1000                        let next_token2 =1001                            Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span);1002                        self.bump_with((next_token2, self.token_spacing));1003                        self.bump();1004                        let base1 =1005                            self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None);1006                        self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix)1007                    }1008                    DestructuredFloat::Error => base,1009                })1010            }1011            _ => {1012                self.error_unexpected_after_dot();1013                Ok(base)1014            }1015        }1016    }10171018    fn error_unexpected_after_dot(&self) {1019        let actual = super::token_descr(&self.token);1020        let span = self.token.span;1021        let sm = self.psess.source_map();1022        let (span, actual) = match (&self.token.kind, self.subparser_name) {1023            (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => {1024                (span.shrink_to_hi(), format!("`{}`", snippet))1025            }1026            (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => {1027                // No need to report an error. This case will only occur when parsing a pasted1028                // metavariable, and we should have emitted an error when parsing the macro call in1029                // the first place. E.g. in this code:1030                // ```1031                // macro_rules! m { ($e:expr) => { $e }; }1032                //1033                // fn main() {1034                //     let f = 1;1035                //     m!(f.);1036                // }1037                // ```1038                // we'll get an error "unexpected token: `)` when parsing the `m!(f.)`, so we don't1039                // want to issue a second error when parsing the expansion `«f.»` (where `«`/`»`1040                // represent the invisible delimiters).1041                self.dcx().span_delayed_bug(span, "bad dot expr in metavariable");1042                return;1043            }1044            _ => (span, actual),1045        };1046        self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual });1047    }10481049    /// We need an identifier or integer, but the next token is a float.1050    /// Break the float into components to extract the identifier or integer.1051    ///1052    /// See also [`TokenKind::break_two_token_op`] which does similar splitting of `>>` into `>`.1053    //1054    // FIXME: With current `TokenCursor` it's hard to break tokens into more than 21055    //  parts unless those parts are processed immediately. `TokenCursor` should either1056    //  support pushing "future tokens" (would be also helpful to `break_and_eat`), or1057    //  we should break everything including floats into more basic proc-macro style1058    //  tokens in the lexer (probably preferable).1059    pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {1060        #[derive(Debug)]1061        enum FloatComponent {1062            IdentLike(String),1063            Punct(char),1064        }1065        use FloatComponent::*;10661067        let float_str = float.as_str();1068        let mut components = Vec::new();1069        let mut ident_like = String::new();1070        for c in float_str.chars() {1071            if c == '_' || c.is_ascii_alphanumeric() {1072                ident_like.push(c);1073            } else if matches!(c, '.' | '+' | '-') {1074                if !ident_like.is_empty() {1075                    components.push(IdentLike(mem::take(&mut ident_like)));1076                }1077                components.push(Punct(c));1078            } else {1079                panic!("unexpected character in a float token: {c:?}")1080            }1081        }1082        if !ident_like.is_empty() {1083            components.push(IdentLike(ident_like));1084        }10851086        // With proc macros the span can refer to anything, the source may be too short,1087        // or too long, or non-ASCII. It only makes sense to break our span into components1088        // if its underlying text is identical to our float literal.1089        let can_take_span_apart =1090            || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref();10911092        match &*components {1093            // 1e21094            [IdentLike(i)] => {1095                DestructuredFloat::Single(Symbol::intern(i), span)1096            }1097            // 1.1098            [IdentLike(left), Punct('.')] => {1099                let (left_span, dot_span) = if can_take_span_apart() {1100                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1101                    let dot_span = span.with_lo(left_span.hi());1102                    (left_span, dot_span)1103                } else {1104                    (span, span)1105                };1106                let left = Symbol::intern(left);1107                DestructuredFloat::TrailingDot(left, left_span, dot_span)1108            }1109            // 1.2 | 1.2e31110            [IdentLike(left), Punct('.'), IdentLike(right)] => {1111                let (left_span, dot_span, right_span) = if can_take_span_apart() {1112                    let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));1113                    let dot_span = span.with_lo(left_span.hi()).with_hi(left_span.hi() + BytePos(1));1114                    let right_span = span.with_lo(dot_span.hi());1115                    (left_span, dot_span, right_span)1116                } else {1117                    (span, span, span)1118                };1119                let left = Symbol::intern(left);1120                let right = Symbol::intern(right);1121                DestructuredFloat::MiddleDot(left, left_span, dot_span, right, right_span)1122            }1123            // 1e+ | 1e- (recovered)1124            [IdentLike(_), Punct('+' | '-')] |1125            // 1e+2 | 1e-21126            [IdentLike(_), Punct('+' | '-'), IdentLike(_)] |1127            // 1.2e+ | 1.2e-1128            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] |1129            // 1.2e+3 | 1.2e-31130            [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {1131                // See the FIXME about `TokenCursor` above.1132                self.error_unexpected_after_dot();1133                DestructuredFloat::Error1134            }1135            _ => panic!("unexpected components in a float token: {components:?}"),1136        }1137    }11381139    /// Parse the field access used in offset_of, matched by `$(e:expr)+`.1140    /// Currently returns a list of idents. However, it should be possible in1141    /// future to also do array indices, which might be arbitrary expressions.1142    pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {1143        let mut fields = Vec::new();1144        let mut trailing_dot = None;11451146        loop {1147            // This is expected to use a metavariable $(args:expr)+, but the builtin syntax1148            // could be called directly. Calling `parse_expr` allows this function to only1149            // consider `Expr`s.1150            let expr = self.parse_expr()?;1151            let mut current = &expr;1152            let start_idx = fields.len();1153            loop {1154                match current.kind {1155                    ExprKind::Field(ref left, right) => {1156                        // Field access is read right-to-left.1157                        fields.insert(start_idx, right);1158                        trailing_dot = None;1159                        current = left;1160                    }1161                    // Parse this both to give helpful error messages and to1162                    // verify it can be done with this parser setup.1163                    ExprKind::Index(ref left, ref _right, span) => {1164                        self.dcx().emit_err(errors::ArrayIndexInOffsetOf(span));1165                        current = left;1166                    }1167                    ExprKind::Lit(token::Lit {1168                        kind: token::Float | token::Integer,1169                        symbol,1170                        suffix,1171                    }) => {1172                        if let Some(suffix) = suffix {1173                            self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {1174                                span: current.span,1175                                suffix,1176                            });1177                        }1178                        match self.break_up_float(symbol, current.span) {1179                            // 1e21180                            DestructuredFloat::Single(sym, sp) => {1181                                trailing_dot = None;1182                                fields.insert(start_idx, Ident::new(sym, sp));1183                            }1184                            // 1.1185                            DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {1186                                assert!(suffix.is_none());1187                                trailing_dot = Some(dot_span);1188                                fields.insert(start_idx, Ident::new(sym, sym_span));1189                            }1190                            // 1.2 | 1.2e31191                            DestructuredFloat::MiddleDot(1192                                symbol1,1193                                span1,1194                                _dot_span,1195                                symbol2,1196                                span2,1197                            ) => {1198                                trailing_dot = None;1199                                fields.insert(start_idx, Ident::new(symbol2, span2));1200                                fields.insert(start_idx, Ident::new(symbol1, span1));1201                            }1202                            DestructuredFloat::Error => {1203                                trailing_dot = None;1204                                fields.insert(start_idx, Ident::new(symbol, self.prev_token.span));1205                            }1206                        }1207                        break;1208                    }1209                    ExprKind::Path(None, Path { ref segments, .. }) => {1210                        match &segments[..] {1211                            [PathSegment { ident, args: None, .. }] => {1212                                trailing_dot = None;1213                                fields.insert(start_idx, *ident)1214                            }1215                            _ => {1216                                self.dcx().emit_err(errors::InvalidOffsetOf(current.span));1217                                break;1218                            }1219                        }1220                        break;1221                    }1222                    _ => {1223                        self.dcx().emit_err(errors::InvalidOffsetOf(current.span));1224                        break;1225                    }1226                }1227            }12281229            if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma {1230                break;1231            } else if trailing_dot.is_none() {1232                // This loop should only repeat if there is a trailing dot.1233                self.dcx().emit_err(errors::InvalidOffsetOf(self.token.span));1234                break;1235            }1236        }1237        if let Some(dot) = trailing_dot {1238            self.dcx().emit_err(errors::InvalidOffsetOf(dot));1239        }1240        Ok(fields.into_iter().collect())1241    }12421243    fn mk_expr_tuple_field_access(1244        &self,1245        lo: Span,1246        ident_span: Span,1247        base: Box<Expr>,1248        field: Symbol,1249        suffix: Option<Symbol>,1250    ) -> Box<Expr> {1251        if let Some(suffix) = suffix {1252            self.dcx()1253                .emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix });1254        }1255        self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span)))1256    }12571258    /// Parse a function call expression, `expr(...)`.1259    fn parse_expr_fn_call(&mut self, lo: Span, fun: Box<Expr>) -> Box<Expr> {1260        let snapshot = if self.token == token::OpenParen {1261            Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))1262        } else {1263            None1264        };1265        let open_paren = self.token.span;12661267        let seq = self1268            .parse_expr_paren_seq()1269            .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)));1270        match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) {1271            Ok(expr) => expr,1272            Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err),1273        }1274    }12751276    /// If we encounter a parser state that looks like the user has written a `struct` literal with1277    /// parentheses instead of braces, recover the parser state and provide suggestions.1278    #[instrument(skip(self, seq, snapshot), level = "trace")]1279    fn maybe_recover_struct_lit_bad_delims(1280        &mut self,1281        lo: Span,1282        open_paren: Span,1283        seq: PResult<'a, Box<Expr>>,1284        snapshot: Option<(SnapshotParser<'a>, ExprKind)>,1285    ) -> PResult<'a, Box<Expr>> {1286        match (self.may_recover(), seq, snapshot) {1287            (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {1288                snapshot.bump(); // `(`1289                match snapshot.parse_struct_fields(path.clone(), false, exp!(CloseParen)) {1290                    Ok((fields, ..)) if snapshot.eat(exp!(CloseParen)) => {1291                        // We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest1292                        // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.1293                        self.restore_snapshot(snapshot);1294                        let close_paren = self.prev_token.span;1295                        let span = lo.to(close_paren);1296                        // filter shorthand fields1297                        let fields: Vec<_> =1298                            fields.into_iter().filter(|field| !field.is_shorthand).collect();12991300                        let guar = if !fields.is_empty() &&1301                            // `token.kind` should not be compared here.1302                            // This is because the `snapshot.token.kind` is treated as the same as1303                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even1304                            // if they are different.1305                            self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")1306                        {1307                            err.cancel();1308                            let type_str = pprust::path_to_string(&path);1309                            self.dcx()1310                                .create_err(errors::ParenthesesWithStructFields {1311                                    span,1312                                    braces_for_struct: errors::BracesForStructLiteral {1313                                        first: open_paren,1314                                        second: close_paren,1315                                        r#type: type_str.clone(),1316                                    },1317                                    no_fields_for_fn: errors::NoFieldsForFnCall {1318                                        r#type: type_str,1319                                        fields: fields1320                                            .into_iter()1321                                            .map(|field| field.span.until(field.expr.span))1322                                            .collect(),1323                                    },1324                                })1325                                .emit()1326                        } else {1327                            err.emit()1328                        };1329                        Ok(self.mk_expr_err(span, guar))1330                    }1331                    Ok(_) => Err(err),1332                    Err(err2) => {1333                        err2.cancel();1334                        Err(err)1335                    }1336                }1337            }1338            (_, seq, _) => seq,1339        }1340    }13411342    /// Parse an indexing expression `expr[...]`.1343    fn parse_expr_index(&mut self, lo: Span, base: Box<Expr>) -> PResult<'a, Box<Expr>> {1344        let prev_span = self.prev_token.span;1345        let open_delim_span = self.token.span;1346        self.bump(); // `[`1347        let index = self.parse_expr()?;1348        self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?;1349        self.expect(exp!(CloseBracket))?;1350        Ok(self.mk_expr(1351            lo.to(self.prev_token.span),1352            self.mk_index(base, index, open_delim_span.to(self.prev_token.span)),1353        ))1354    }13551356    /// Assuming we have just parsed `.`, continue parsing into an expression.1357    fn parse_dot_suffix(&mut self, self_arg: Box<Expr>, lo: Span) -> PResult<'a, Box<Expr>> {1358        if self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) {1359            return Ok(self.mk_await_expr(self_arg, lo));1360        }13611362        if self.eat_keyword(exp!(Use)) {1363            let use_span = self.prev_token.span;1364            self.psess.gated_spans.gate(sym::ergonomic_clones, use_span);1365            return Ok(self.mk_use_expr(self_arg, lo));1366        }13671368        // Post-fix match1369        if self.eat_keyword(exp!(Match)) {1370            let match_span = self.prev_token.span;1371            self.psess.gated_spans.gate(sym::postfix_match, match_span);1372            return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);1373        }13741375        // Parse a postfix `yield`.1376        if self.eat_keyword(exp!(Yield)) {1377            let yield_span = self.prev_token.span;1378            self.psess.gated_spans.gate(sym::yield_expr, yield_span);1379            return Ok(1380                self.mk_expr(lo.to(yield_span), ExprKind::Yield(YieldKind::Postfix(self_arg)))1381            );1382        }13831384        let fn_span_lo = self.token.span;1385        let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;1386        self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]);1387        self.check_turbofish_missing_angle_brackets(&mut seg);13881389        if self.check(exp!(OpenParen)) {1390            // Method call `expr.f()`1391            let args = self.parse_expr_paren_seq()?;1392            let fn_span = fn_span_lo.to(self.prev_token.span);1393            let span = lo.to(self.prev_token.span);1394            Ok(self.mk_expr(1395                span,1396                ExprKind::MethodCall(Box::new(ast::MethodCall {1397                    seg,1398                    receiver: self_arg,1399                    args,1400                    span: fn_span,1401                })),1402            ))1403        } else {1404            // Field access `expr.f`1405            let span = lo.to(self.prev_token.span);1406            if let Some(args) = seg.args {1407                // See `StashKey::GenericInFieldExpr` for more info on why we stash this.1408                self.dcx()1409                    .create_err(errors::FieldExpressionWithGeneric(args.span()))1410                    .stash(seg.ident.span, StashKey::GenericInFieldExpr);1411            }14121413            Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident)))1414        }1415    }14161417    /// At the bottom (top?) of the precedence hierarchy,1418    /// Parses things like parenthesized exprs, macros, `return`, etc.1419    ///1420    /// N.B., this does not parse outer attributes, and is private because it only works1421    /// correctly if called from `parse_expr_dot_or_call`.1422    fn parse_expr_bottom(&mut self) -> PResult<'a, Box<Expr>> {1423        maybe_recover_from_interpolated_ty_qpath!(self, true);14241425        let span = self.token.span;1426        if let Some(expr) = self.eat_metavar_seq_with_matcher(1427            |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),1428            |this| {1429                // Force collection (as opposed to just `parse_expr`) is required to avoid the1430                // attribute duplication seen in #138478.1431                let expr = this.parse_expr_force_collect();1432                // FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly1433                // related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line1434                // `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in1435                // `compiler/rustc_index/src/bit_set/tests.rs`.1436                if this.token.kind == token::Comma {1437                    this.bump();1438                }1439                expr1440            },1441        ) {1442            return Ok(expr);1443        } else if let Some(lit) =1444            self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())1445        {1446            return Ok(lit);1447        } else if let Some(block) =1448            self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block())1449        {1450            return Ok(self.mk_expr(span, ExprKind::Block(block, None)));1451        } else if let Some(path) =1452            self.eat_metavar_seq(MetaVarKind::Path, |this| this.parse_path(PathStyle::Type))1453        {1454            return Ok(self.mk_expr(span, ExprKind::Path(None, path)));1455        }14561457        // Outer attributes are already parsed and will be1458        // added to the return value after the fact.14591460        let restrictions = self.restrictions;1461        self.with_res(restrictions - Restrictions::ALLOW_LET, |this| {1462            // Note: adding new syntax here? Don't forget to adjust `TokenKind::can_begin_expr()`.1463            let lo = this.token.span;1464            if let token::Literal(_) = this.token.kind {1465                // This match arm is a special-case of the `_` match arm below and1466                // could be removed without changing functionality, but it's faster1467                // to have it here, especially for programs with large constants.1468                this.parse_expr_lit()1469            } else if this.check(exp!(OpenParen)) {1470                this.parse_expr_tuple_parens(restrictions)1471            } else if this.check(exp!(OpenBrace)) {1472                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {1473                    return Ok(expr);1474                }1475                this.parse_expr_block(None, lo, BlockCheckMode::Default)1476            } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {1477                this.parse_expr_closure().map_err(|mut err| {1478                    // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`1479                    // then suggest parens around the lhs.1480                    if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {1481                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));1482                    }1483                    err1484                })1485            } else if this.check(exp!(OpenBracket)) {1486                this.parse_expr_array_or_repeat(exp!(CloseBracket))1487            } else if this.is_builtin() {1488                this.parse_expr_builtin()1489            } else if this.check_path() {1490                this.parse_expr_path_start()1491            } else if this.check_keyword(exp!(Move))1492                || this.check_keyword(exp!(Use))1493                || this.check_keyword(exp!(Static))1494                || this.check_const_closure()1495            {1496                this.parse_expr_closure()1497            } else if this.eat_keyword(exp!(If)) {1498                this.parse_expr_if()1499            } else if this.check_keyword(exp!(For)) {1500                if this.choose_generics_over_qpath(1) {1501                    this.parse_expr_closure()1502                } else {1503                    assert!(this.eat_keyword(exp!(For)));1504                    this.parse_expr_for(None, lo)1505                }1506            } else if this.eat_keyword(exp!(While)) {1507                this.parse_expr_while(None, lo)1508            } else if let Some(label) = this.eat_label() {1509                this.parse_expr_labeled(label, true)1510            } else if this.eat_keyword(exp!(Loop)) {1511                this.parse_expr_loop(None, lo).map_err(|mut err| {1512                    err.span_label(lo, "while parsing this `loop` expression");1513                    err1514                })1515            } else if this.eat_keyword(exp!(Match)) {1516                this.parse_expr_match().map_err(|mut err| {1517                    err.span_label(lo, "while parsing this `match` expression");1518                    err1519                })1520            } else if this.eat_keyword(exp!(Unsafe)) {1521                this.parse_expr_block(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err(1522                    |mut err| {1523                        err.span_label(lo, "while parsing this `unsafe` expression");1524                        err1525                    },1526                )1527            } else if this.check_inline_const(0) {1528                this.parse_const_block(lo, false)1529            } else if this.may_recover() && this.is_do_catch_block() {1530                this.recover_do_catch()1531            } else if this.is_try_block() {1532                this.expect_keyword(exp!(Try))?;1533                this.parse_try_block(lo)1534            } else if this.eat_keyword(exp!(Return)) {1535                this.parse_expr_return()1536            } else if this.eat_keyword(exp!(Continue)) {1537                this.parse_expr_continue(lo)1538            } else if this.eat_keyword(exp!(Break)) {1539                this.parse_expr_break()1540            } else if this.eat_keyword(exp!(Yield)) {1541                this.parse_expr_yield()1542            } else if this.is_do_yeet() {1543                this.parse_expr_yeet()1544            } else if this.eat_keyword(exp!(Become)) {1545                this.parse_expr_become()1546            } else if this.check_keyword(exp!(Let)) {1547                this.parse_expr_let(restrictions)1548            } else if this.eat_keyword(exp!(Underscore)) {1549                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(true)? {1550                    return Ok(expr);1551                }1552                Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))1553            } else if this.token_uninterpolated_span().at_least_rust_2018() {1554                // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.1555                let at_async = this.check_keyword(exp!(Async));1556                // check for `gen {}` and `gen move {}`1557                // or `async gen {}` and `async gen move {}`1558                // FIXME: (async) gen closures aren't yet parsed.1559                // FIXME(gen_blocks): Parse `gen async` and suggest swap1560                if this.token_uninterpolated_span().at_least_rust_2024()1561                    && this.is_gen_block(kw::Gen, at_async as usize)1562                {1563                    this.parse_gen_block()1564                // Check for `async {` and `async move {`,1565                } else if this.is_gen_block(kw::Async, 0) {1566                    this.parse_gen_block()1567                } else if at_async {1568                    this.parse_expr_closure()1569                } else if this.eat_keyword_noexpect(kw::Await) {1570                    this.recover_incorrect_await_syntax(lo)1571                } else {1572                    this.parse_expr_lit()1573                }1574            } else {1575                this.parse_expr_lit()1576            }1577        })1578    }15791580    fn parse_expr_lit(&mut self) -> PResult<'a, Box<Expr>> {1581        let lo = self.token.span;1582        match self.parse_opt_token_lit() {1583            Some((token_lit, _)) => {1584                let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit));1585                self.maybe_recover_from_bad_qpath(expr)1586            }1587            None => self.try_macro_suggestion(),1588        }1589    }15901591    fn parse_expr_tuple_parens(&mut self, restrictions: Restrictions) -> PResult<'a, Box<Expr>> {1592        let lo = self.token.span;1593        self.expect(exp!(OpenParen))?;1594        let (es, trailing_comma) = match self.parse_seq_to_end(1595            exp!(CloseParen),1596            SeqSep::trailing_allowed(exp!(Comma)),1597            |p| p.parse_expr_catch_underscore(restrictions.intersection(Restrictions::ALLOW_LET)),1598        ) {1599            Ok(x) => x,1600            Err(err) => {1601                return Ok(self.recover_seq_parse_error(1602                    exp!(OpenParen),1603                    exp!(CloseParen),1604                    lo,1605                    err,1606                ));1607            }1608        };1609        let kind = if es.len() == 1 && matches!(trailing_comma, Trailing::No) {1610            // `(e)` is parenthesized `e`.1611            ExprKind::Paren(es.into_iter().next().unwrap())1612        } else {1613            // `(e,)` is a tuple with only one field, `e`.1614            ExprKind::Tup(es)1615        };1616        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1617        self.maybe_recover_from_bad_qpath(expr)1618    }16191620    fn parse_expr_array_or_repeat(&mut self, close: ExpTokenPair) -> PResult<'a, Box<Expr>> {1621        let lo = self.token.span;1622        self.bump(); // `[` or other open delim16231624        let kind = if self.eat(close) {1625            // Empty vector1626            ExprKind::Array(ThinVec::new())1627        } else {1628            // Non-empty vector1629            let first_expr = self.parse_expr()?;1630            if self.eat(exp!(Semi)) {1631                // Repeating array syntax: `[ 0; 512 ]`1632                let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?;1633                self.expect(close)?;1634                ExprKind::Repeat(first_expr, count)1635            } else if self.eat(exp!(Comma)) {1636                // Vector with two or more elements.1637                let sep = SeqSep::trailing_allowed(exp!(Comma));1638                let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;1639                exprs.insert(0, first_expr);1640                ExprKind::Array(exprs)1641            } else {1642                // Vector with one element1643                self.expect(close)?;1644                ExprKind::Array(thin_vec![first_expr])1645            }1646        };1647        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1648        self.maybe_recover_from_bad_qpath(expr)1649    }16501651    fn parse_expr_path_start(&mut self) -> PResult<'a, Box<Expr>> {1652        let maybe_eq_tok = self.prev_token;1653        let (qself, path) = if self.eat_lt() {1654            let lt_span = self.prev_token.span;1655            let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {1656                // Suggests using '<=' if there is an error parsing qpath when the previous token1657                // is an '=' token. Only emits suggestion if the '<' token and '=' token are1658                // directly adjacent (i.e. '=<')1659                if maybe_eq_tok == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {1660                    let eq_lt = maybe_eq_tok.span.to(lt_span);1661                    err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);1662                }1663                err1664            })?;1665            (Some(qself), path)1666        } else {1667            (None, self.parse_path(PathStyle::Expr)?)1668        };16691670        // `!`, as an operator, is prefix, so we know this isn't that.1671        let (span, kind) = if self.eat(exp!(Bang)) {1672            // MACRO INVOCATION expression1673            if qself.is_some() {1674                self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span));1675            }1676            let lo = path.span;1677            let mac = Box::new(MacCall { path, args: self.parse_delim_args()? });1678            (lo.to(self.prev_token.span), ExprKind::MacCall(mac))1679        } else if self.check(exp!(OpenBrace))1680            && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)1681        {1682            if qself.is_some() {1683                self.psess.gated_spans.gate(sym::more_qualified_paths, path.span);1684            }1685            return expr;1686        } else {1687            (path.span, ExprKind::Path(qself, path))1688        };16891690        let expr = self.mk_expr(span, kind);1691        self.maybe_recover_from_bad_qpath(expr)1692    }16931694    /// Parse `'label: $expr`. The label is already parsed.1695    pub(super) fn parse_expr_labeled(1696        &mut self,1697        label_: Label,1698        mut consume_colon: bool,1699    ) -> PResult<'a, Box<Expr>> {1700        let lo = label_.ident.span;1701        let label = Some(label_);1702        let ate_colon = self.eat(exp!(Colon));1703        let tok_sp = self.token.span;1704        let expr = if self.eat_keyword(exp!(While)) {1705            self.parse_expr_while(label, lo)1706        } else if self.eat_keyword(exp!(For)) {1707            self.parse_expr_for(label, lo)1708        } else if self.eat_keyword(exp!(Loop)) {1709            self.parse_expr_loop(label, lo)1710        } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() {1711            self.parse_expr_block(label, lo, BlockCheckMode::Default)1712        } else if !ate_colon1713            && self.may_recover()1714            && (self.token.kind.close_delim().is_some() || self.token.is_punct())1715            && could_be_unclosed_char_literal(label_.ident)1716        {1717            let (lit, _) =1718                self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {1719                    self_.dcx().create_err(errors::UnexpectedTokenAfterLabel {1720                        span: self_.token.span,1721                        remove_label: None,1722                        enclose_in_block: None,1723                    })1724                });1725            consume_colon = false;1726            Ok(self.mk_expr(lo, ExprKind::Lit(lit)))1727        } else if !ate_colon1728            && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))1729        {1730            // We're probably inside of a `Path<'a>` that needs a turbofish1731            let guar = self.dcx().emit_err(errors::UnexpectedTokenAfterLabel {1732                span: self.token.span,1733                remove_label: None,1734                enclose_in_block: None,1735            });1736            consume_colon = false;1737            Ok(self.mk_expr_err(lo, guar))1738        } else {1739            let mut err = errors::UnexpectedTokenAfterLabel {1740                span: self.token.span,1741                remove_label: None,1742                enclose_in_block: None,1743            };17441745            // Continue as an expression in an effort to recover on `'label: non_block_expr`.1746            let expr = self.parse_expr().map(|expr| {1747                let span = expr.span;17481749                let found_labeled_breaks = {1750                    struct FindLabeledBreaksVisitor;17511752                    impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {1753                        type Result = ControlFlow<()>;1754                        fn visit_expr(&mut self, ex: &'ast Expr) -> ControlFlow<()> {1755                            if let ExprKind::Break(Some(_label), _) = ex.kind {1756                                ControlFlow::Break(())1757                            } else {1758                                walk_expr(self, ex)1759                            }1760                        }1761                    }17621763                    FindLabeledBreaksVisitor.visit_expr(&expr).is_break()1764                };17651766                // Suggestion involves adding a labeled block.1767                //1768                // If there are no breaks that may use this label, suggest removing the label and1769                // recover to the unmodified expression.1770                if !found_labeled_breaks {1771                    err.remove_label = Some(lo.until(span));17721773                    return expr;1774                }17751776                err.enclose_in_block = Some(errors::UnexpectedTokenAfterLabelSugg {1777                    left: span.shrink_to_lo(),1778                    right: span.shrink_to_hi(),1779                });17801781                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.1782                let stmt = self.mk_stmt(span, StmtKind::Expr(expr));1783                let blk = self.mk_block(thin_vec![stmt], BlockCheckMode::Default, span);1784                self.mk_expr(span, ExprKind::Block(blk, label))1785            });17861787            self.dcx().emit_err(err);1788            expr1789        }?;17901791        if !ate_colon && consume_colon {1792            self.dcx().emit_err(errors::RequireColonAfterLabeledExpression {1793                span: expr.span,1794                label: lo,1795                label_end: lo.between(tok_sp),1796            });1797        }17981799        Ok(expr)1800    }18011802    /// Emit an error when a char is parsed as a lifetime or label because of a missing quote.1803    pub(super) fn recover_unclosed_char<L>(1804        &self,1805        ident: Ident,1806        mk_lit_char: impl FnOnce(Symbol, Span) -> L,1807        err: impl FnOnce(&Self) -> Diag<'a>,1808    ) -> L {1809        assert!(could_be_unclosed_char_literal(ident));1810        self.dcx()1811            .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| {1812                err.span_suggestion_verbose(1813                    ident.span.shrink_to_hi(),1814                    "add `'` to close the char literal",1815                    "'",1816                    Applicability::MaybeIncorrect,1817                );1818            })1819            .unwrap_or_else(|| {1820                err(self)1821                    .with_span_suggestion_verbose(1822                        ident.span.shrink_to_hi(),1823                        "add `'` to close the char literal",1824                        "'",1825                        Applicability::MaybeIncorrect,1826                    )1827                    .emit()1828            });1829        let name = ident.without_first_quote().name;1830        mk_lit_char(name, ident.span)1831    }18321833    /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.1834    fn recover_do_catch(&mut self) -> PResult<'a, Box<Expr>> {1835        let lo = self.token.span;18361837        self.bump(); // `do`1838        self.bump(); // `catch`18391840        let span = lo.to(self.prev_token.span);1841        self.dcx().emit_err(errors::DoCatchSyntaxRemoved { span });18421843        self.parse_try_block(lo)1844    }18451846    /// Parse an expression if the token can begin one.1847    fn parse_expr_opt(&mut self) -> PResult<'a, Option<Box<Expr>>> {1848        Ok(if self.token.can_begin_expr() { Some(self.parse_expr()?) } else { None })1849    }18501851    /// Parse `"return" expr?`.1852    fn parse_expr_return(&mut self) -> PResult<'a, Box<Expr>> {1853        let lo = self.prev_token.span;1854        let kind = ExprKind::Ret(self.parse_expr_opt()?);1855        let expr = self.mk_expr(lo.to(self.prev_token.span), kind);1856        self.maybe_recover_from_bad_qpath(expr)1857    }18581859    /// Parse `"do" "yeet" expr?`.1860    fn parse_expr_yeet(&mut self) -> PResult<'a, Box<Expr>> {1861        let lo = self.token.span;18621863        self.bump(); // `do`1864        self.bump(); // `yeet`18651866        let kind = ExprKind::Yeet(self.parse_expr_opt()?);18671868        let span = lo.to(self.prev_token.span);1869        self.psess.gated_spans.gate(sym::yeet_expr, span);1870        let expr = self.mk_expr(span, kind);1871        self.maybe_recover_from_bad_qpath(expr)1872    }18731874    /// Parse `"become" expr`, with `"become"` token already eaten.1875    fn parse_expr_become(&mut self) -> PResult<'a, Box<Expr>> {1876        let lo = self.prev_token.span;1877        let kind = ExprKind::Become(self.parse_expr()?);1878        let span = lo.to(self.prev_token.span);1879        self.psess.gated_spans.gate(sym::explicit_tail_calls, span);1880        let expr = self.mk_expr(span, kind);1881        self.maybe_recover_from_bad_qpath(expr)1882    }18831884    /// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.1885    /// If the label is followed immediately by a `:` token, the label and `:` are1886    /// parsed as part of the expression (i.e. a labeled loop). The language team has1887    /// decided in #87026 to require parentheses as a visual aid to avoid confusion if1888    /// the break expression of an unlabeled break is a labeled loop (as in1889    /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value1890    /// expression only gets a warning for compatibility reasons; and a labeled break1891    /// with a labeled loop does not even get a warning because there is no ambiguity.1892    fn parse_expr_break(&mut self) -> PResult<'a, Box<Expr>> {1893        let lo = self.prev_token.span;1894        let mut label = self.eat_label();1895        let kind = if self.token == token::Colon1896            && let Some(label) = label.take()1897        {1898            // The value expression can be a labeled loop, see issue #86948, e.g.:1899            // `loop { break 'label: loop { break 'label 42; }; }`1900            let lexpr = self.parse_expr_labeled(label, true)?;1901            self.dcx().emit_err(errors::LabeledLoopInBreak {1902                span: lexpr.span,1903                sub: errors::WrapInParentheses::Expression {1904                    left: lexpr.span.shrink_to_lo(),1905                    right: lexpr.span.shrink_to_hi(),1906                },1907            });1908            Some(lexpr)1909        } else if self.token != token::OpenBrace1910            || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)1911        {1912            let mut expr = self.parse_expr_opt()?;1913            if let Some(expr) = &mut expr {1914                if label.is_some()1915                    && match &expr.kind {1916                        ExprKind::While(_, _, None)1917                        | ExprKind::ForLoop { label: None, .. }1918                        | ExprKind::Loop(_, None, _) => true,1919                        ExprKind::Block(block, None) => {1920                            matches!(block.rules, BlockCheckMode::Default)1921                        }1922                        _ => false,1923                    }1924                {1925                    let span = expr.span;1926                    self.psess.buffer_lint(1927                        BREAK_WITH_LABEL_AND_LOOP,1928                        lo.to(expr.span),1929                        ast::CRATE_NODE_ID,1930                        errors::BreakWithLabelAndLoop {1931                            sub: errors::BreakWithLabelAndLoopSub {1932                                left: span.shrink_to_lo(),1933                                right: span.shrink_to_hi(),1934                            },1935                        },1936                    );1937                }19381939                // Recover `break label aaaaa`1940                if self.may_recover()1941                    && let ExprKind::Path(None, p) = &expr.kind1942                    && let [segment] = &*p.segments1943                    && let &ast::PathSegment { ident, args: None, .. } = segment1944                    && let Some(next) = self.parse_expr_opt()?1945                {1946                    label = Some(self.recover_ident_into_label(ident));1947                    *expr = next;1948                }1949            }19501951            expr1952        } else {1953            None1954        };1955        let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind));1956        self.maybe_recover_from_bad_qpath(expr)1957    }19581959    /// Parse `"continue" label?`.1960    fn parse_expr_continue(&mut self, lo: Span) -> PResult<'a, Box<Expr>> {1961        let mut label = self.eat_label();19621963        // Recover `continue label` -> `continue 'label`1964        if self.may_recover()1965            && label.is_none()1966            && let Some((ident, _)) = self.token.ident()1967        {1968            self.bump();1969            label = Some(self.recover_ident_into_label(ident));1970        }19711972        let kind = ExprKind::Continue(label);1973        Ok(self.mk_expr(lo.to(self.prev_token.span), kind))1974    }19751976    /// Parse `"yield" expr?`.1977    fn parse_expr_yield(&mut self) -> PResult<'a, Box<Expr>> {1978        let lo = self.prev_token.span;1979        let kind = ExprKind::Yield(YieldKind::Prefix(self.parse_expr_opt()?));1980        let span = lo.to(self.prev_token.span);1981        self.psess.gated_spans.gate(sym::yield_expr, span);1982        let expr = self.mk_expr(span, kind);1983        self.maybe_recover_from_bad_qpath(expr)1984    }19851986    /// Parse `builtin # ident(args,*)`.1987    fn parse_expr_builtin(&mut self) -> PResult<'a, Box<Expr>> {1988        self.parse_builtin(|this, lo, ident| {1989            Ok(match ident.name {1990                sym::offset_of => Some(this.parse_expr_offset_of(lo)?),1991                sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?),1992                sym::wrap_binder => {1993                    Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?)1994                }1995                sym::unwrap_binder => {1996                    Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?)1997                }1998                _ => None,1999            })2000        })

Findings

✓ No findings reported for this file.

Get this view in your editor

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