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