Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress irrefutable let patterns lint for prefixes in match guards #103031

Merged
merged 1 commit into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
15 changes: 12 additions & 3 deletions src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down