diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 279 |
1 files changed, 154 insertions, 125 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9c457f150a3..f3b53971b29 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; +use rustc_span::edition::Edition; use rustc_span::source_map::{self, Spanned}; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -344,7 +345,7 @@ impl<'a> Parser<'a> { fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { self.dcx().emit_err(errors::FoundExprWouldBeStmt { span: self.token.span, - token: self.token.clone(), + token: self.token, suggestion: ExprParenthesesNeeded::surrounding(lhs.span), }); } @@ -417,7 +418,7 @@ impl<'a> Parser<'a> { cur_op_span: Span, ) -> PResult<'a, P<Expr>> { let rhs = if self.is_at_start_of_range_notation_rhs() { - let maybe_lt = self.token.clone(); + let maybe_lt = self.token; let attrs = self.parse_outer_attributes()?; Some( self.parse_expr_assoc_with(Bound::Excluded(prec), attrs) @@ -436,7 +437,7 @@ impl<'a> Parser<'a> { fn is_at_start_of_range_notation_rhs(&self) -> bool { if self.token.can_begin_expr() { // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. - if self.token == token::OpenDelim(Delimiter::Brace) { + if self.token == token::OpenBrace { return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); } true @@ -542,8 +543,8 @@ impl<'a> Parser<'a> { } // Recover from `++x`: token::Plus if this.look_ahead(1, |t| *t == token::Plus) => { - let starts_stmt = this.prev_token == token::Semi - || this.prev_token == token::CloseDelim(Delimiter::Brace); + let starts_stmt = + this.prev_token == token::Semi || this.prev_token == token::CloseBrace; let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span)); // Eat both `+`s. this.bump(); @@ -611,7 +612,7 @@ impl<'a> Parser<'a> { /// Recover on `not expr` in favor of `!expr`. fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - let negated_token = self.look_ahead(1, |t| t.clone()); + let negated_token = self.look_ahead(1, |t| *t); let sub_diag = if negated_token.is_numeric_lit() { errors::NotAsNegationOperatorSub::SuggestNotBitwise @@ -637,10 +638,8 @@ impl<'a> Parser<'a> { /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { match self.prev_token.kind { - TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) | TokenKind::Interpolated(..) => { - self.prev_token.span - } - TokenKind::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => { + token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span, + token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => { // `expr.span` is the interpolated span, because invisible open // and close delims both get marked with the same span, one // that covers the entire thing between them. (See @@ -829,6 +828,18 @@ impl<'a> Parser<'a> { if let Some(lt) = lifetime { self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span)); } + + // Add expected tokens if we parsed `&raw` as an expression. + // This will make sure we see "expected `const`, `mut`", and + // guides recovery in case we write `&raw expr`. + if borrow_kind == ast::BorrowKind::Ref + && mutbl == ast::Mutability::Not + && matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw)) + { + self.expected_token_types.insert(TokenType::KwMut); + self.expected_token_types.insert(TokenType::KwConst); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) } @@ -902,8 +913,8 @@ impl<'a> Parser<'a> { return Ok(e); } e = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), - token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, + token::OpenParen => self.parse_expr_fn_call(lo, e), + token::OpenBracket => self.parse_expr_index(lo, e)?, _ => return Ok(e), } } @@ -992,7 +1003,7 @@ impl<'a> Parser<'a> { (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => { (span.shrink_to_hi(), format!("`{}`", snippet)) } - (token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))), _) => { + (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => { // No need to report an error. This case will only occur when parsing a pasted // metavariable, and we should have emitted an error when parsing the macro call in // the first place. E.g. in this code: @@ -1192,7 +1203,7 @@ impl<'a> Parser<'a> { } } - if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) { + if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma { break; } else if trailing_dot.is_none() { // This loop should only repeat if there is a trailing dot. @@ -1222,7 +1233,7 @@ impl<'a> Parser<'a> { /// Parse a function call expression, `expr(...)`. fn parse_expr_fn_call(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> { - let snapshot = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let snapshot = if self.token == token::OpenParen { Some((self.create_snapshot_for_diagnostic(), fun.kind.clone())) } else { None @@ -1386,15 +1397,7 @@ impl<'a> Parser<'a> { maybe_recover_from_interpolated_ty_qpath!(self, true); let span = self.token.span; - if let token::Interpolated(nt) = &self.token.kind { - match &**nt { - token::NtBlock(block) => { - let block = block.clone(); - self.bump(); - return Ok(self.mk_expr(self.prev_token.span, ExprKind::Block(block, None))); - } - }; - } else if let Some(expr) = self.eat_metavar_seq_with_matcher( + if let Some(expr) = self.eat_metavar_seq_with_matcher( |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }), |this| { // Force collection (as opposed to just `parse_expr`) is required to avoid the @@ -1415,9 +1418,13 @@ impl<'a> Parser<'a> { self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) { return Ok(lit); - } else if let Some(path) = self.eat_metavar_seq(MetaVarKind::Path, |this| { - this.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type)) - }) { + } else if let Some(block) = + self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block()) + { + return Ok(self.mk_expr(span, ExprKind::Block(block, None))); + } else if let Some(path) = + self.eat_metavar_seq(MetaVarKind::Path, |this| this.parse_path(PathStyle::Type)) + { return Ok(self.mk_expr(span, ExprKind::Path(None, path))); } @@ -1612,7 +1619,7 @@ impl<'a> Parser<'a> { } fn parse_expr_path_start(&mut self) -> PResult<'a, P<Expr>> { - let maybe_eq_tok = self.prev_token.clone(); + let maybe_eq_tok = self.prev_token; let (qself, path) = if self.eat_lt() { let lt_span = self.prev_token.span; let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| { @@ -1670,14 +1677,11 @@ impl<'a> Parser<'a> { self.parse_expr_for(label, lo) } else if self.eat_keyword(exp!(Loop)) { self.parse_expr_loop(label, lo) - } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) - || self.token.is_whole_block() - { + } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() { self.parse_expr_block(label, lo, BlockCheckMode::Default) } else if !ate_colon && self.may_recover() - && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) - || self.token.is_punct()) + && (self.token.kind.close_delim().is_some() || self.token.is_punct()) && could_be_unclosed_char_literal(label_.ident) { let (lit, _) = @@ -1872,19 +1876,21 @@ impl<'a> Parser<'a> { }, }); Some(lexpr) - } else if self.token != token::OpenDelim(Delimiter::Brace) + } else if self.token != token::OpenBrace || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { let mut expr = self.parse_expr_opt()?; if let Some(expr) = &mut expr { if label.is_some() - && matches!( - expr.kind, + && match &expr.kind { ExprKind::While(_, _, None) - | ExprKind::ForLoop { label: None, .. } - | ExprKind::Loop(_, None, _) - | ExprKind::Block(_, None) - ) + | ExprKind::ForLoop { label: None, .. } + | ExprKind::Loop(_, None, _) => true, + ExprKind::Block(block, None) => { + matches!(block.rules, BlockCheckMode::Default) + } + _ => false, + } { self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, @@ -2008,7 +2014,7 @@ impl<'a> Parser<'a> { // Eat tokens until the macro call ends. if self.may_recover() { - while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) { + while !self.token.kind.is_close_delim_or_eof() { self.bump(); } } @@ -2073,7 +2079,7 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { - let token = self.token.clone(); + let token = self.token; let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); self_.dcx().struct_span_err(token.span, msg) @@ -2140,6 +2146,17 @@ impl<'a> Parser<'a> { /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and /// `Lit::from_token` (excluding unary negation). fn eat_token_lit(&mut self) -> Option<token::Lit> { + let check_expr = |expr: P<Expr>| { + if let ast::ExprKind::Lit(token_lit) = expr.kind { + Some(token_lit) + } else if let ast::ExprKind::Unary(UnOp::Neg, inner) = &expr.kind + && let ast::Expr { kind: ast::ExprKind::Lit(_), .. } = **inner + { + None + } else { + panic!("unexpected reparsed expr/literal: {:?}", expr.kind); + } + }; match self.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) if name.is_bool_lit() => { self.bump(); @@ -2149,27 +2166,19 @@ impl<'a> Parser<'a> { self.bump(); Some(token_lit) } - token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( - MetaVarKind::Literal, - ))) => { + token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Literal)) => { let lit = self .eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) .expect("metavar seq literal"); - let ast::ExprKind::Lit(token_lit) = lit.kind else { - panic!("didn't reparse a literal"); - }; - Some(token_lit) + check_expr(lit) } - token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + token::OpenInvisible(InvisibleOrigin::MetaVar( mv_kind @ MetaVarKind::Expr { can_begin_literal_maybe_minus: true, .. }, - ))) => { + )) => { let expr = self .eat_metavar_seq(mv_kind, |this| this.parse_expr()) .expect("metavar seq expr"); - let ast::ExprKind::Lit(token_lit) = expr.kind else { - panic!("didn't reparse an expr"); - }; - Some(token_lit) + check_expr(expr) } _ => None, } @@ -2262,7 +2271,7 @@ impl<'a> Parser<'a> { } fn is_array_like_block(&mut self) -> bool { - matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace)) + self.token.kind == TokenKind::OpenBrace && self .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) && self.look_ahead(2, |t| t == &token::Comma) @@ -2315,8 +2324,8 @@ impl<'a> Parser<'a> { |p| p.parse_expr(), ) { Ok(_) - // When the close delim is `)`, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`, - // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`. + // When the close delim is `)`, `token.kind` is expected to be `token::CloseParen`, + // but the actual `token.kind` is `token::CloseBracket`. // This is because the `token.kind` of the close delim is treated as the same as // that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different. // Therefore, `token.kind` should not be compared here. @@ -2349,7 +2358,7 @@ impl<'a> Parser<'a> { } } - if self.token.is_whole_block() { + if self.token.is_metavar_block() { self.dcx().emit_err(errors::InvalidBlockMacroSegment { span: self.token.span, context: lo.to(self.token.span), @@ -2374,7 +2383,7 @@ impl<'a> Parser<'a> { fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; - let before = self.prev_token.clone(); + let before = self.prev_token; let binder = if self.check_keyword(exp!(For)) { let lo = self.token.span; let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; @@ -2406,8 +2415,8 @@ impl<'a> Parser<'a> { FnRetTy::Default(_) => { let restrictions = self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; - let prev = self.prev_token.clone(); - let token = self.token.clone(); + let prev = self.prev_token; + let token = self.token; let attrs = self.parse_outer_attributes()?; match self.parse_expr_res(restrictions, attrs) { Ok((expr, _)) => expr, @@ -2471,8 +2480,8 @@ impl<'a> Parser<'a> { fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> { if self.may_recover() && self.token.can_begin_expr() - && !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace)) - && !self.token.is_whole_block() + && self.token.kind != TokenKind::OpenBrace + && !self.token.is_metavar_block() { let snapshot = self.create_snapshot_for_diagnostic(); let restrictions = @@ -2594,7 +2603,10 @@ impl<'a> Parser<'a> { /// Parses an `if` expression (`if` token already eaten). fn parse_expr_if(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; - let cond = self.parse_expr_cond()?; + // Scoping code checks the top level edition of the `if`; let's match it here. + // The `CondChecker` also checks the edition of the `let` itself, just to make sure. + let let_chains_policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(let_chains_policy)?; self.parse_if_after_cond(lo, cond) } @@ -2654,7 +2666,7 @@ impl<'a> Parser<'a> { } } else { let attrs = self.parse_outer_attributes()?; // For recovery. - let maybe_fatarrow = self.token.clone(); + let maybe_fatarrow = self.token; let block = if self.check(exp!(OpenBrace)) { self.parse_block()? } else if let Some(block) = recover_block_from_condition(self) { @@ -2703,18 +2715,17 @@ impl<'a> Parser<'a> { } /// Parses the condition of a `if` or `while` expression. + /// + /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, + /// i.e. the same span we use to later decide whether the drop behaviour should be that of + /// edition `..=2021` or that of `2024..`. // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. - pub fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> { + pub fn parse_expr_cond(&mut self, let_chains_policy: LetChainsPolicy) -> PResult<'a, P<Expr>> { let attrs = self.parse_outer_attributes()?; let (mut cond, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; - CondChecker::new(self).visit_expr(&mut cond); - - if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + CondChecker::new(self, let_chains_policy).visit_expr(&mut cond); Ok(cond) } @@ -2874,7 +2885,7 @@ impl<'a> Parser<'a> { } fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> { - let begin_paren = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. @@ -2908,7 +2919,7 @@ impl<'a> Parser<'a> { return Err(err); } }; - return if self.token == token::CloseDelim(Delimiter::Parenthesis) { + return if self.token == token::CloseParen { // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the // parser state and emit a targeted suggestion. let span = vec![start_span, self.token.span]; @@ -2952,7 +2963,7 @@ impl<'a> Parser<'a> { let (pat, expr) = self.parse_for_head()?; // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) - && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace)) + && self.token.kind != token::OpenBrace && self.may_recover() { let guar = self @@ -3009,7 +3020,8 @@ impl<'a> Parser<'a> { /// Parses a `while` or `while let` expression (`while` token already eaten). fn parse_expr_while(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { - let cond = self.parse_expr_cond().map_err(|mut err| { + let policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(policy).map_err(|mut err| { err.span_label(lo, "while parsing the condition of this `while` expression"); err })?; @@ -3101,7 +3113,7 @@ impl<'a> Parser<'a> { let attrs = self.parse_inner_attributes()?; let mut arms = ThinVec::new(); - while self.token != token::CloseDelim(Delimiter::Brace) { + while self.token != token::CloseBrace { match self.parse_arm() { Ok(arm) => arms.push(arm), Err(e) => { @@ -3109,7 +3121,7 @@ impl<'a> Parser<'a> { let guar = e.emit(); self.recover_stmt(); let span = lo.to(self.token.span); - if self.token == token::CloseDelim(Delimiter::Brace) { + if self.token == token::CloseBrace { self.bump(); } // Always push at least one arm to make the match non-empty @@ -3170,7 +3182,7 @@ impl<'a> Parser<'a> { // We might have either a `,` -> `;` typo, or a block without braces. We need // a more subtle parsing strategy. loop { - if self.token == token::CloseDelim(Delimiter::Brace) { + if self.token == token::CloseBrace { // We have reached the closing brace of the `match` expression. return Some(err(self, stmts)); } @@ -3229,7 +3241,7 @@ impl<'a> Parser<'a> { // this avoids the compiler saying that a `,` or `}` was expected even though // the pattern isn't a never pattern (and thus an arm body is required) let armless = (!is_fat_arrow && !is_almost_fat_arrow && pat.could_be_never_pattern()) - || matches!(this.token.kind, token::Comma | token::CloseDelim(Delimiter::Brace)); + || matches!(this.token.kind, token::Comma | token::CloseBrace); let mut result = if armless { // A pattern without a body, allowed for never patterns. @@ -3277,8 +3289,8 @@ impl<'a> Parser<'a> { err })?; - let require_comma = !classify::expr_is_complete(&expr) - && this.token != token::CloseDelim(Delimiter::Brace); + let require_comma = + !classify::expr_is_complete(&expr) && this.token != token::CloseBrace; if !require_comma { arm_body = Some(expr); @@ -3393,17 +3405,17 @@ impl<'a> Parser<'a> { } fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<P<Expr>>> { - // Used to check the `let_chains` and `if_let_guard` features mostly by scanning + // Used to check the `if_let_guard` feature mostly by scanning // `&&` tokens. - fn check_let_expr(expr: &Expr) -> (bool, bool) { + fn has_let_expr(expr: &Expr) -> bool { match &expr.kind { ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => { - let lhs_rslt = check_let_expr(lhs); - let rhs_rslt = check_let_expr(rhs); - (lhs_rslt.0 || rhs_rslt.0, false) + let lhs_rslt = has_let_expr(lhs); + let rhs_rslt = has_let_expr(rhs); + lhs_rslt || rhs_rslt } - ExprKind::Let(..) => (true, true), - _ => (false, true), + ExprKind::Let(..) => true, + _ => false, } } if !self.eat_keyword(exp!(If)) { @@ -3414,14 +3426,9 @@ impl<'a> Parser<'a> { let if_span = self.prev_token.span; let mut cond = self.parse_match_guard_condition()?; - CondChecker::new(self).visit_expr(&mut cond); + CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); - let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); - if has_let_expr { - if does_not_have_bin_op { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + if has_let_expr(&cond) { let span = if_span.to(cond.span); self.psess.gated_spans.gate(sym::if_let_guard, span); } @@ -3429,7 +3436,7 @@ impl<'a> Parser<'a> { } fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> { - if self.token == token::OpenDelim(Delimiter::Parenthesis) { + if self.token == token::OpenParen { let left = self.token.span; let pat = self.parse_pat_no_top_guard( None, @@ -3448,7 +3455,7 @@ impl<'a> Parser<'a> { unreachable!() }; self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); - CondChecker::new(self).visit_expr(&mut cond); + CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); let right = self.prev_token.span; self.dcx().emit_err(errors::ParenthesesInMatchPat { span: vec![left, right], @@ -3475,7 +3482,7 @@ impl<'a> Parser<'a> { match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) { Ok((expr, _)) => Ok(expr), Err(mut err) => { - if self.prev_token == token::OpenDelim(Delimiter::Brace) { + if self.prev_token == token::OpenBrace { let sugg_sp = self.prev_token.span.shrink_to_lo(); // Consume everything within the braces, let's avoid further parse // errors. @@ -3518,8 +3525,7 @@ impl<'a> Parser<'a> { fn is_do_catch_block(&self) -> bool { self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Catch]) - && self - .look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) + && self.look_ahead(2, |t| *t == token::OpenBrace || t.is_metavar_block()) && !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) } @@ -3529,8 +3535,7 @@ impl<'a> Parser<'a> { fn is_try_block(&self) -> bool { self.token.is_keyword(kw::Try) - && self - .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) + && self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block()) && self.token_uninterpolated_span().at_least_rust_2018() } @@ -3564,13 +3569,11 @@ impl<'a> Parser<'a> { // `async move {` self.is_keyword_ahead(lookahead + 1, &[kw::Move, kw::Use]) && self.look_ahead(lookahead + 2, |t| { - *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block() + *t == token::OpenBrace || t.is_metavar_block() }) ) || ( // `async {` - self.look_ahead(lookahead + 1, |t| { - *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block() - }) + self.look_ahead(lookahead + 1, |t| *t == token::OpenBrace || t.is_metavar_block()) )) } @@ -3694,11 +3697,7 @@ impl<'a> Parser<'a> { AssocOp::from_token(t).is_some() || matches!( t.kind, - token::OpenDelim( - Delimiter::Parenthesis - | Delimiter::Bracket - | Delimiter::Brace - ) + token::OpenParen | token::OpenBracket | token::OpenBrace ) || *t == token::Dot }) @@ -3855,14 +3854,14 @@ impl<'a> Parser<'a> { t == &token::Colon || t == &token::Eq || t == &token::Comma - || t == &token::CloseDelim(Delimiter::Brace) - || t == &token::CloseDelim(Delimiter::Parenthesis) + || t == &token::CloseBrace + || t == &token::CloseParen }); if is_wrong { return Err(this.dcx().create_err(errors::ExpectedStructField { span: this.look_ahead(1, |t| t.span), ident_span: this.token.span, - token: this.look_ahead(1, |t| t.clone()), + token: this.look_ahead(1, |t| *t), })); } let (ident, expr) = if is_shorthand { @@ -4027,7 +4026,14 @@ pub(crate) enum ForbiddenLetReason { NotSupportedParentheses(#[primary_span] Span), } -/// Visitor to check for invalid/unstable use of `ExprKind::Let` that can't +/// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on +/// 2024 and later). In case of edition dependence, specify the currently present edition. +pub enum LetChainsPolicy { + AlwaysAllowed, + EditionDependent { current_edition: Edition }, +} + +/// Visitor to check for invalid use of `ExprKind::Let` that can't /// easily be caught in parsing. For example: /// /// ```rust,ignore (example) @@ -4038,35 +4044,57 @@ pub(crate) enum ForbiddenLetReason { /// ``` struct CondChecker<'a> { parser: &'a Parser<'a>, + let_chains_policy: LetChainsPolicy, + depth: u32, forbid_let_reason: Option<ForbiddenLetReason>, missing_let: Option<errors::MaybeMissingLet>, comparison: Option<errors::MaybeComparison>, } impl<'a> CondChecker<'a> { - fn new(parser: &'a Parser<'a>) -> Self { - CondChecker { parser, forbid_let_reason: None, missing_let: None, comparison: None } + fn new(parser: &'a Parser<'a>, let_chains_policy: LetChainsPolicy) -> Self { + CondChecker { + parser, + forbid_let_reason: None, + missing_let: None, + comparison: None, + let_chains_policy, + depth: 0, + } } } impl MutVisitor for CondChecker<'_> { fn visit_expr(&mut self, e: &mut P<Expr>) { + self.depth += 1; use ForbiddenLetReason::*; let span = e.span; match e.kind { ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { - *recovered = Recovered::Yes(self.parser.dcx().emit_err( - errors::ExpectedExpressionFoundLet { + let error = match reason { + NotSupportedOr(or_span) => { + self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span }) + } + _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { span, reason, missing_let: self.missing_let, comparison: self.comparison, - }, - )); - } else { - self.parser.psess.gated_spans.gate(sym::let_chains, span); + }), + }; + *recovered = Recovered::Yes(error); + } else if self.depth > 1 { + // Top level `let` is always allowed; only gate chains + match self.let_chains_policy { + LetChainsPolicy::AlwaysAllowed => (), + LetChainsPolicy::EditionDependent { current_edition } => { + if !current_edition.at_least_rust_2024() || !span.at_least_rust_2024() { + self.parser.psess.gated_spans.gate(sym::let_chains, span); + } + } + } } } ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, _, _) => { @@ -4168,5 +4196,6 @@ impl MutVisitor for CondChecker<'_> { // These would forbid any let expressions they contain already. } } + self.depth -= 1; } } |
