about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-06-26 19:47:00 +0200
committerGitHub <noreply@github.com>2022-06-26 19:47:00 +0200
commit5b312710d509e75b5d52b77d56e2d34ec2038696 (patch)
tree73932778e750edb5faea162fd871a29fca656fb5 /compiler/rustc_parse/src/parser/expr.rs
parentc348beacea13b417f71f2b80534a9afb18a69c3e (diff)
parent747586732b1bb21c1ef318428fe179a77db2d963 (diff)
downloadrust-5b312710d509e75b5d52b77d56e2d34ec2038696.tar.gz
rust-5b312710d509e75b5d52b77d56e2d34ec2038696.zip
Rollup merge of #97295 - c410-f3r:yet-another-let-chain, r=compiler-errors
[rustc_parse] Forbid `let`s in certain places

Currently only forbids in locals to resolve https://github.com/rust-lang/rust/pull/94927#issuecomment-1099605024 but feel free to point any other places.
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs30
1 files changed, 27 insertions, 3 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 81bab0e3513..2c43563b104 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -7,6 +7,7 @@ use super::{
 };
 use crate::maybe_recover_from_interpolated_ty_qpath;
 
+use core::mem;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::Spacing;
@@ -26,7 +27,6 @@ use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_span::source_map::{self, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
-use std::mem;
 
 /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
 /// dropped into the token stream, which happens while parsing the result of
@@ -2343,7 +2343,9 @@ impl<'a> Parser<'a> {
 
     /// Parses the condition of a `if` or `while` expression.
     fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
-        let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        let cond = self.with_let_management(true, |local_self| {
+            local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)
+        })?;
 
         if let ExprKind::Let(..) = cond.kind {
             // Remove the last feature gating of a `let` expression since it's stable.
@@ -2356,6 +2358,13 @@ impl<'a> Parser<'a> {
     /// Parses a `let $pat = $expr` pseudo-expression.
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+        if !self.let_expr_allowed {
+            self.struct_span_err(
+                self.prev_token.span,
+                "expected expression, found `let` statement",
+            )
+            .emit();
+        }
         let lo = self.prev_token.span;
         let pat = self.parse_pat_allow_top_alt(
             None,
@@ -2672,6 +2681,8 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
+        // Used to check the `let_chains` and `if_let_guard` features mostly by scaning
+        // `&&` tokens.
         fn check_let_expr(expr: &Expr) -> (bool, bool) {
             match expr.kind {
                 ExprKind::Binary(_, ref lhs, ref rhs) => {
@@ -2694,7 +2705,7 @@ impl<'a> Parser<'a> {
             )?;
             let guard = if this.eat_keyword(kw::If) {
                 let if_span = this.prev_token.span;
-                let cond = this.parse_expr()?;
+                let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?;
                 let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
                 if has_let_expr {
                     if does_not_have_bin_op {
@@ -3256,4 +3267,17 @@ impl<'a> Parser<'a> {
             Ok((res, trailing))
         })
     }
+
+    // Calls `f` with the internal `let_expr_allowed` set to `let_expr_allowed` and then
+    // sets the internal `let_expr_allowed` back to its original value.
+    fn with_let_management<T>(
+        &mut self,
+        let_expr_allowed: bool,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        let last_let_expr_allowed = mem::replace(&mut self.let_expr_allowed, let_expr_allowed);
+        let rslt = f(self);
+        self.let_expr_allowed = last_let_expr_allowed;
+        rslt
+    }
 }