about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs18
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr22
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs15
3 files changed, 36 insertions, 19 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index ec709a1db51..5984c800d83 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -370,8 +370,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
 
             // Check if the let source is while, for there is no alternative place to put a prefix,
             // and we shouldn't lint.
+            // For let guards inside a match, prefixes might use bindings of the match pattern,
+            // so can't always be moved out.
+            // FIXME: Add checking whether the bindings are actually used in the prefix,
+            // and lint if they are not.
             let let_source = let_source_parent(self.tcx, top, None);
-            if !matches!(let_source, LetSource::WhileLet) {
+            if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
                 // Emit the lint
                 let prefix = &chain_refutabilities[..until];
                 lint_affix(prefix, "leading", "outside of the construct");
@@ -1151,10 +1155,14 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
 
     let parent_parent = hir.get_parent_node(parent);
     let parent_parent_node = hir.get(parent_parent);
-    if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) =
-        parent_parent_node
-    {
-        return LetSource::LetElse(*span);
+    match parent_parent_node {
+        hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) => {
+            return LetSource::LetElse(*span);
+        }
+        hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::If(_)), .. }) => {
+            return LetSource::IfLetGuard;
+        }
+        _ => {}
     }
 
     let parent_parent_parent = hir.get_parent_node(parent_parent);
diff --git a/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr
index 2570c36b810..be4a5231558 100644
--- a/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr
@@ -75,26 +75,26 @@ LL |     if let first = &opt && let None = Some(1) {}
    = note: this pattern will always match
    = help: consider moving it outside of the construct
 
-error: irrefutable `let` patterns
+error: irrefutable `if let` guard patterns
   --> $DIR/irrefutable-lets.rs:44:28
    |
 LL |         Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {},
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: these patterns will always match, so the `let` is useless
-   = help: consider removing `let`
+   = note: these patterns will always match, so the guard is useless
+   = help: consider removing the guard and adding a `let` inside the match arm
 
-error: leading irrefutable pattern in let chain
-  --> $DIR/irrefutable-lets.rs:50:28
+error: trailing irrefutable patterns in let chain
+  --> $DIR/irrefutable-lets.rs:59:16
    |
-LL |         Some(ref first) if let Range { start: local_start, end: _ } = first
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             && let v = local_end && let w = v => {},
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: this pattern will always match
-   = help: consider moving it outside of the construct
+   = note: these patterns will always match
+   = help: consider moving them into the body
 
 error: irrefutable `while let` patterns
-  --> $DIR/irrefutable-lets.rs:59:11
+  --> $DIR/irrefutable-lets.rs:68:11
    |
 LL |     while let first = &opt && let (a, b) = (1, 2) {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -103,7 +103,7 @@ LL |     while let first = &opt && let (a, b) = (1, 2) {}
    = help: consider instead using a `loop { ... }` with a `let` inside it
 
 error: trailing irrefutable patterns in let chain
-  --> $DIR/irrefutable-lets.rs:62:40
+  --> $DIR/irrefutable-lets.rs:71:40
    |
 LL |     while let Some(ref first) = opt && let second = first && let _third = second {}
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs
index 3d1626e8ffb..9afb6853b36 100644
--- a/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs
@@ -42,18 +42,27 @@ fn main() {
 
     match opt {
         Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {},
-        //[disallowed]~^ ERROR irrefutable `let` patterns
+        //[disallowed]~^ ERROR irrefutable `if let` guard patterns
         _ => {}
     }
 
+    // No error about leading irrefutable patterns: the expr on the rhs might
+    // use the bindings created by the match.
     match opt {
         Some(ref first) if let Range { start: local_start, end: _ } = first
-        //[disallowed]~^ ERROR leading irrefutable pattern in let chain
             && let None = local_start => {},
         _ => {}
     }
 
-    // No error, despite the prefix being irrefutable
+    match opt {
+        Some(ref first) if let Range { start: Some(_), end: local_end } = first
+            && let v = local_end && let w = v => {},
+        //[disallowed]~^ ERROR trailing irrefutable patterns in let chain
+        _ => {}
+    }
+
+    // No error, despite the prefix being irrefutable: moving out could change the behaviour,
+    // due to possible side effects of the operation.
     while let first = &opt && let Some(ref second) = first && let None = second.start {}
 
     while let first = &opt && let (a, b) = (1, 2) {}