1
Fork 0

Rollup merge of #103031 - est31:match_guard_irrefutable_let, r=oli-obk

Suppress irrefutable let patterns lint for prefixes in match guards

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.

Fixes #98361
This commit is contained in:
Dylan DPC 2022-10-14 16:19:16 +05:30 committed by GitHub
commit 7cf09c57a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 19 deletions

View file

@ -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, // Check if the let source is while, for there is no alternative place to put a prefix,
// and we shouldn't lint. // 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); 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 // Emit the lint
let prefix = &chain_refutabilities[..until]; let prefix = &chain_refutabilities[..until];
lint_affix(prefix, "leading", "outside of the construct"); lint_affix(prefix, "leading", "outside of the construct");
@ -1151,11 +1155,15 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
let parent_parent = hir.get_parent_node(parent); let parent_parent = hir.get_parent_node(parent);
let parent_parent_node = hir.get(parent_parent); let parent_parent_node = hir.get(parent_parent);
if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = match parent_parent_node {
parent_parent_node hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) => {
{
return LetSource::LetElse(*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); let parent_parent_parent = hir.get_parent_node(parent_parent);
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);

View file

@ -75,26 +75,26 @@ LL | if let first = &opt && let None = Some(1) {}
= note: this pattern will always match = note: this pattern will always match
= help: consider moving it outside of the construct = help: consider moving it outside of the construct
error: irrefutable `let` patterns error: irrefutable `if let` guard patterns
--> $DIR/irrefutable-lets.rs:44:28 --> $DIR/irrefutable-lets.rs:44:28
| |
LL | Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {}, 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 = note: these patterns will always match, so the guard is useless
= help: consider removing `let` = help: consider removing the guard and adding a `let` inside the match arm
error: leading irrefutable pattern in let chain error: trailing irrefutable patterns in let chain
--> $DIR/irrefutable-lets.rs:50:28 --> $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 = note: these patterns will always match
= help: consider moving it outside of the construct = help: consider moving them into the body
error: irrefutable `while let` patterns 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) {} 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 = help: consider instead using a `loop { ... }` with a `let` inside it
error: trailing irrefutable patterns in let chain 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 {} LL | while let Some(ref first) = opt && let second = first && let _third = second {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -42,18 +42,27 @@ fn main() {
match opt { match opt {
Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {}, 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 { match opt {
Some(ref first) if let Range { start: local_start, end: _ } = first Some(ref first) if let Range { start: local_start, end: _ } = first
//[disallowed]~^ ERROR leading irrefutable pattern in let chain
&& let None = local_start => {}, && 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 Some(ref second) = first && let None = second.start {}
while let first = &opt && let (a, b) = (1, 2) {} while let first = &opt && let (a, b) = (1, 2) {}