]> git.lizzy.rs Git - rust.git/commitdiff
Suppress irrefutable let patterns lint for prefixes in match guards
authorest31 <MTest31@outlook.com>
Thu, 13 Oct 2022 23:46:24 +0000 (01:46 +0200)
committerest31 <MTest31@outlook.com>
Fri, 14 Oct 2022 00:16:40 +0000 (02:16 +0200)
In match guards, irrefutable prefixes might use the bindings created
by the match pattern. Ideally, we check for this, but we can do the
next best thing and just not lint for irrefutable prefixes in match
guards.

compiler/rustc_mir_build/src/thir/pattern/check_match.rs
src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr
src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs

index ec709a1db513e0ef43277aeea699bb552feb45cf..5984c800d8381ee16e026d849e589247cf6d19c8 100644 (file)
@@ -370,8 +370,12 @@ fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId)
 
             // 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);
index 2570c36b8108e0ec186f7a484a656a0493274b17..be4a523155825a0795e9c9fb4c89ef434dd020db 100644 (file)
@@ -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 {}
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 3d1626e8ffb9adb95afc00a8db2aa8e40279be6e..9afb6853b362752bc954bba811dae86aaf1babba 100644 (file)
@@ -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) {}