diff --git a/src/expr.rs b/src/expr.rs
index 9493822f5a..80a4b1a1e0 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -3253,30 +3253,12 @@ pub(crate) mod printing {
         };
 
         // These cases require parenthesization independently of precedence.
-        match (&*e.left, &e.op) {
+        if let BinOp::Lt(_) | BinOp::Shl(_) = &e.op {
             // `x as i32 < y` has the parser thinking that `i32 < y` is the
             // beginning of a path type. It starts trying to parse `x as (i32 <
             // y ...` instead of `(x as i32) < ...`. We need to convince it
             // _not_ to do that.
-            (_, BinOp::Lt(_) | BinOp::Shl(_)) if classify::confusable_with_adjacent_lt(&e.left) => {
-                left_needs_group = true;
-            }
-
-            // We are given `(let _ = a) OP b`.
-            //
-            // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid
-            //   redundant parens as the parser will interpret this as `(let _ =
-            //   a) OP b`.
-            //
-            // - Otherwise, e.g. when we have `(let a = b) < c` in AST, parens
-            //   are required since the parser would interpret `let a = b < c`
-            //   as `let a = (b < c)`. To achieve this, we force parens.
-            #[cfg(feature = "full")]
-            (Expr::Let(_), _) if binop_prec > Precedence::And => {
-                left_needs_group = true;
-            }
-
-            _ => {}
+            left_needs_group |= classify::confusable_with_adjacent_lt(&e.left);
         }
 
         print_subexpression(
diff --git a/src/fixup.rs b/src/fixup.rs
index ddee906fb5..536da4e499 100644
--- a/src/fixup.rs
+++ b/src/fixup.rs
@@ -239,7 +239,7 @@ impl FixupContext {
     ///     "let chain".
     pub fn needs_group_as_let_scrutinee(self, expr: &Expr) -> bool {
         self.parenthesize_exterior_struct_lit && classify::confusable_with_adjacent_block(expr)
-            || self.trailing_precedence(expr) <= Precedence::And
+            || self.trailing_precedence(expr) < Precedence::Let
     }
 
     /// Determines the effective precedence of a left subexpression. Some
@@ -265,7 +265,11 @@ impl FixupContext {
             match expr {
                 // Increase precedence of expressions that extend to the end of
                 // current statement or group.
-                Expr::Break(_) | Expr::Closure(_) | Expr::Return(_) | Expr::Yield(_) => {
+                Expr::Break(_)
+                | Expr::Closure(_)
+                | Expr::Let(_)
+                | Expr::Return(_)
+                | Expr::Yield(_) => {
                     return Precedence::Prefix;
                 }
                 Expr::Range(e) if e.start.is_none() => return Precedence::Prefix,
diff --git a/src/precedence.rs b/src/precedence.rs
index 354ea90812..1a26f195db 100644
--- a/src/precedence.rs
+++ b/src/precedence.rs
@@ -19,6 +19,9 @@ pub(crate) enum Precedence {
     Or,
     // &&
     And,
+    // let
+    #[cfg(feature = "printing")]
+    Let,
     // == != < > <= >=
     Compare,
     // |
@@ -97,8 +100,9 @@ impl Precedence {
             Expr::Assign(_) => Precedence::Assign,
             Expr::Range(_) => Precedence::Range,
             Expr::Binary(e) => Precedence::of_binop(&e.op),
+            Expr::Let(_) => Precedence::Let,
             Expr::Cast(_) => Precedence::Cast,
-            Expr::Let(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
+            Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
 
             Expr::Array(_)
             | Expr::Async(_)