Skip to content
Open
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
16 changes: 0 additions & 16 deletions compiler/rustc_mir_build/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,22 +765,6 @@ pub(crate) struct LowerRangeBoundMustBeLessThanUpper {
pub(crate) span: Span,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_leading_irrefutable_let_patterns)]
#[note]
#[help]
pub(crate) struct LeadingIrrefutableLetPatterns {
pub(crate) count: usize,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_trailing_irrefutable_let_patterns)]
#[note]
#[help]
pub(crate) struct TrailingIrrefutableLetPatterns {
pub(crate) count: usize,
}

#[derive(LintDiagnostic)]
#[diag(mir_build_bindings_with_variant_name, code = E0170)]
pub(crate) struct BindingsWithVariantName {
Expand Down
69 changes: 6 additions & 63 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
{
let mut chain_refutabilities = Vec::new();
let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
// If at least one of the operands is a `let ... = ...`.
if chain_refutabilities.iter().any(|x| x.is_some()) {
self.check_let_chain(chain_refutabilities, ex.span);
// Check only single let binding
if let [Some((_, refutability))] = chain_refutabilities[..] {
self.check_single_let(refutability, ex.span);
}
return;
}
Expand Down Expand Up @@ -562,73 +562,16 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
}

#[instrument(level = "trace", skip(self))]
fn check_let_chain(
&mut self,
chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
whole_chain_span: Span,
) {
fn check_single_let(&mut self, refutability: RefutableFlag, whole_chain_span: Span) {
assert!(self.let_source != LetSource::None);

if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
// The entire chain is made up of irrefutable `let` statements
if matches!(refutability, Irrefutable) {
report_irrefutable_let_patterns(
self.tcx,
self.lint_level,
self.let_source,
chain_refutabilities.len(),
1,
whole_chain_span,
);
return;
}

if let Some(until) =
chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
&& until > 0
{
// The chain has a non-zero prefix of irrefutable `let` statements.

// 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.
// For `else if let`, an extra indentation level would be required to move the bindings.
// FIXME: Add checking whether the bindings are actually used in the prefix,
// and lint if they are not.
if !matches!(
self.let_source,
LetSource::WhileLet | LetSource::IfLetGuard | LetSource::ElseIfLet
) {
// Emit the lint
let prefix = &chain_refutabilities[..until];
let span_start = prefix[0].unwrap().0;
let span_end = prefix.last().unwrap().unwrap().0;
let span = span_start.to(span_end);
let count = prefix.len();
self.tcx.emit_node_span_lint(
IRREFUTABLE_LET_PATTERNS,
self.lint_level,
span,
LeadingIrrefutableLetPatterns { count },
);
}
}

if let Some(from) =
chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
&& from != (chain_refutabilities.len() - 1)
{
// The chain has a non-empty suffix of irrefutable `let` statements
let suffix = &chain_refutabilities[from + 1..];
let span_start = suffix[0].unwrap().0;
let span_end = suffix.last().unwrap().unwrap().0;
let span = span_start.to(span_end);
let count = suffix.len();
self.tcx.emit_node_span_lint(
IRREFUTABLE_LET_PATTERNS,
self.lint_level,
span,
TrailingIrrefutableLetPatterns { count },
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/check_alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn insert_alignment_check<'tcx>(

// If this target does not have reliable alignment, further limit the mask by anding it with
// the mask for the highest reliable alignment.
#[allow(irrefutable_let_patterns)]
#[cfg_attr(bootstrap, expect(irrefutable_let_patterns))]
if let max_align = tcx.sess.target.max_reliable_alignment()
&& max_align < Align::MAX
{
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Fallback to shims in submodules.
_ => {
// Math shims
#[expect(irrefutable_let_patterns)]
#[cfg_attr(bootstrap, expect(irrefutable_let_patterns))]
if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
)? && !matches!(res, EmulateItemResult::NotSupported)
Expand Down
122 changes: 122 additions & 0 deletions tests/ui/binding/irrefutable-in-let-chains.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// https://github.com/rust-lang/rust/issues/139369
// Test that the lint `irrefutable_let_patterns` now
// only checks single let binding.
//@ edition: 2024
//@ check-pass

#![feature(if_let_guard)]

use std::ops::Range;

fn main() {
let opt = Some(None..Some(1));

// test `if let`
if let first = &opt {}
//~^ WARN irrefutable `if let` pattern

if let first = &opt && let Some(second) = first {}

if let first = &opt && let (a, b) = (1, 2) {}

if let first = &opt && let None = Some(1) {}

if 4 * 2 == 0 && let first = &opt {}

if let first = &opt
&& let Some(second) = first
&& let None = second.start
&& let v = 0
{}

if let Range { start: local_start, end: _ } = (None..Some(1)) {}
//~^ WARN irrefutable `if let` pattern

if let Range { start: local_start, end: _ } = (None..Some(1))
&& let None = local_start
{}

if let (a, b, c) = (Some(1), Some(1), Some(1)) {}
//~^ WARN irrefutable `if let` pattern

if let (a, b, c) = (Some(1), Some(1), Some(1)) && let None = Some(1) {}

if let Some(ref first) = opt
&& let Range { start: local_start, end: _ } = first
&& let None = local_start
{}

// test `else if let`
if opt == Some(None..None) {
} else if let x = opt.clone().map(|_| 1) {
//~^ WARN irrefutable `if let` pattern
}

if opt == Some(None..None) {
} else if let x = opt.clone().map(|_| 1)
&& x == Some(1)
{}

if opt == Some(None..None) {
} else if opt.is_some() && let x = &opt
{}

if opt == Some(None..None) {
} else {
if let x = opt.clone().map(|_| 1) && x == Some(1)
{}
}

// test `if let guard`
match opt {
Some(ref first) if let second = first => {}
//~^ WARN irrefutable `if let` guard pattern
_ => {}
}

match opt {
Some(ref first)
if let second = first
&& let _third = second
&& let v = 4 + 4 => {}
_ => {}
}

match opt {
Some(ref first)
if let Range { start: local_start, end: _ } = first
&& let None = local_start => {}
_ => {}
}

match opt {
Some(ref first)
if let Range { start: Some(_), end: local_end } = first
&& let v = local_end
&& let w = v => {}
_ => {}
}

// test `while let`
while let first = &opt {}
//~^ WARN irrefutable `while let` pattern

while let first = &opt
&& let (a, b) = (1, 2)
{}

while let first = &opt
&& let Some(second) = first
&& let None = second.start
{}

while let Some(ref first) = opt
&& let second = first
&& let _third = second
{}

while let Some(ref first) = opt
&& let Range { start: local_start, end: _ } = first
&& let None = local_start
{}
}
57 changes: 57 additions & 0 deletions tests/ui/binding/irrefutable-in-let-chains.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
warning: irrefutable `if let` pattern
--> $DIR/irrefutable-in-let-chains.rs:15:8
|
LL | if let first = &opt {}
| ^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default

warning: irrefutable `if let` pattern
--> $DIR/irrefutable-in-let-chains.rs:32:8
|
LL | if let Range { start: local_start, end: _ } = (None..Some(1)) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`

warning: irrefutable `if let` pattern
--> $DIR/irrefutable-in-let-chains.rs:39:8
|
LL | if let (a, b, c) = (Some(1), Some(1), Some(1)) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`

warning: irrefutable `if let` pattern
--> $DIR/irrefutable-in-let-chains.rs:51:15
|
LL | } else if let x = opt.clone().map(|_| 1) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`

warning: irrefutable `if let` guard pattern
--> $DIR/irrefutable-in-let-chains.rs:72:28
|
LL | Some(ref first) if let second = first => {}
| ^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the guard is useless
= help: consider removing the guard and adding a `let` inside the match arm

warning: irrefutable `while let` pattern
--> $DIR/irrefutable-in-let-chains.rs:101:11
|
LL | while let first = &opt {}
| ^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the loop will never exit
= help: consider instead using a `loop { ... }` with a `let` inside it

warning: 6 warnings emitted

Loading
Loading