diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 4a741169443dd..bcdb759d19d50 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -208,19 +208,6 @@ mir_build_irrefutable_let_patterns_while_let = irrefutable `while let` {$count - } will always match, so the loop will never exit .help = consider instead using a `loop {"{"} ... {"}"}` with a `let` inside it -mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count -> - [one] pattern - *[other] patterns - } in let chain - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match - .help = consider moving {$count -> - [one] it - *[other] them - } outside of the construct - mir_build_literal_in_range_out_of_bounds = literal out of range for `{$ty}` .label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}` @@ -339,19 +326,6 @@ mir_build_suggest_let_else = you might want to use `let else` to handle the {$co *[other] variants that aren't } matched -mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> - [one] pattern - *[other] patterns - } in let chain - .note = {$count -> - [one] this pattern - *[other] these patterns - } will always match - .help = consider moving {$count -> - [one] it - *[other] them - } into the body - mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern .label = constant of non-structural type mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 3d9753d72da58..3f68f134c0070 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -776,22 +776,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 { 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 2400d297c89cf..2cf866d92b23a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -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); + // Lint only single irrefutable let binding. + if let [Some((_, Irrefutable))] = chain_refutabilities[..] { + self.lint_single_let(ex.span); } return; } @@ -443,18 +443,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { assert!(self.let_source != LetSource::None); let scrut = scrutinee.map(|id| &self.thir[id]); if let LetSource::PlainLet = self.let_source { - self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span)) - } else { - let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return }; - if matches!(refutability, Irrefutable) { - report_irrefutable_let_patterns( - self.tcx, - self.lint_level, - self.let_source, - 1, - span, - ); - } + self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span)); + } else if let Ok(Irrefutable) = self.is_let_irrefutable(pat, scrut) { + self.lint_single_let(span); } } @@ -562,74 +553,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn check_let_chain( - &mut self, - chain_refutabilities: Vec>, - 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 - report_irrefutable_let_patterns( - self.tcx, - self.lint_level, - self.let_source, - chain_refutabilities.len(), - 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 }, - ); - } + fn lint_single_let(&mut self, let_span: Span) { + report_irrefutable_let_patterns(self.tcx, self.lint_level, self.let_source, 1, let_span); } fn analyze_binding( diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index e06883dd0fd10..5085905aa42b6 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -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 { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 74818cf0740bd..4bb21472dd795 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -817,7 +817,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) diff --git a/tests/ui/binding/irrefutable-in-let-chains.rs b/tests/ui/binding/irrefutable-in-let-chains.rs new file mode 100644 index 0000000000000..eba9947d495b4 --- /dev/null +++ b/tests/ui/binding/irrefutable-in-let-chains.rs @@ -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 + {} +} diff --git a/tests/ui/binding/irrefutable-in-let-chains.stderr b/tests/ui/binding/irrefutable-in-let-chains.stderr new file mode 100644 index 0000000000000..9ac21875e5593 --- /dev/null +++ b/tests/ui/binding/irrefutable-in-let-chains.stderr @@ -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 + diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr deleted file mode 100644 index 008e769cf0b24..0000000000000 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr +++ /dev/null @@ -1,133 +0,0 @@ -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:14:8 - | -LL | if let first = &opt && let Some(second) = first && let None = second.start {} - | ^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it outside of the construct -note: the lint level is defined here - --> $DIR/irrefutable-lets.rs:7:30 - | -LL | #![cfg_attr(disallowed, deny(irrefutable_let_patterns))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: irrefutable `if let` patterns - --> $DIR/irrefutable-lets.rs:20:8 - | -LL | if let first = &opt && let (a, b) = (1, 2) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: these patterns will always match, so the `if let` is useless - = help: consider replacing the `if let` with a `let` - -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:23:8 - | -LL | if let first = &opt && let Some(second) = first && let None = second.start && let v = 0 {} - | ^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it outside of the construct - -error: trailing irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:23:83 - | -LL | if let first = &opt && let Some(second) = first && let None = second.start && let v = 0 {} - | ^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it into the body - -error: trailing irrefutable patterns in let chain - --> $DIR/irrefutable-lets.rs:27:37 - | -LL | if let Some(ref first) = opt && let second = first && let _third = second {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: these patterns will always match - = help: consider moving them into the body - -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:30:8 - | -LL | if let Range { start: local_start, end: _ } = (None..Some(1)) && let None = local_start {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it outside of the construct - -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:33:8 - | -LL | if let (a, b, c) = (Some(1), Some(1), Some(1)) && let None = Some(1) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it outside of the construct - -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:36:8 - | -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 `if let` guard patterns - --> $DIR/irrefutable-lets.rs:45:28 - | -LL | Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {}, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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: trailing irrefutable patterns in let chain - --> $DIR/irrefutable-lets.rs:60:16 - | -LL | && let v = local_end && let w = v => {}, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: these patterns will always match - = help: consider moving them into the body - -error: irrefutable `while let` patterns - --> $DIR/irrefutable-lets.rs:69:11 - | -LL | while let first = &opt && let (a, b) = (1, 2) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: these patterns will always match, so the loop will never exit - = help: consider instead using a `loop { ... }` with a `let` inside it - -error: trailing irrefutable patterns in let chain - --> $DIR/irrefutable-lets.rs:72:40 - | -LL | while let Some(ref first) = opt && let second = first && let _third = second {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: these patterns will always match - = help: consider moving them into the body - -error: trailing irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:88:12 - | -LL | && let x = &opt - | ^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it into the body - -error: leading irrefutable pattern in let chain - --> $DIR/irrefutable-lets.rs:94:12 - | -LL | if let x = opt.clone().map(|_| 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match - = help: consider moving it outside of the construct - -error: aborting due to 14 previous errors - diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs index c8b9ac313ba3f..f379db1e84d65 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs @@ -1,10 +1,7 @@ -//@ revisions: allowed disallowed -//@[allowed] check-pass +//@ check-pass //@ edition: 2024 #![feature(if_let_guard)] -#![cfg_attr(allowed, allow(irrefutable_let_patterns))] -#![cfg_attr(disallowed, deny(irrefutable_let_patterns))] use std::ops::Range; @@ -12,29 +9,21 @@ fn main() { let opt = Some(None..Some(1)); if let first = &opt && let Some(second) = first && let None = second.start {} - //[disallowed]~^ ERROR leading irrefutable pattern in let chain // No lint as the irrefutable pattern is surrounded by other stuff if 4 * 2 == 0 && let first = &opt && let Some(second) = first && let None = second.start {} if let first = &opt && let (a, b) = (1, 2) {} - //[disallowed]~^ ERROR irrefutable `if let` patterns if let first = &opt && let Some(second) = first && let None = second.start && let v = 0 {} - //[disallowed]~^ ERROR leading irrefutable pattern in let chain - //[disallowed]~^^ ERROR trailing irrefutable pattern in let chain if let Some(ref first) = opt && let second = first && let _third = second {} - //[disallowed]~^ ERROR trailing irrefutable patterns in let chain if let Range { start: local_start, end: _ } = (None..Some(1)) && let None = local_start {} - //[disallowed]~^ ERROR leading irrefutable pattern in let chain if let (a, b, c) = (Some(1), Some(1), Some(1)) && let None = Some(1) {} - //[disallowed]~^ ERROR leading irrefutable pattern in let chain if let first = &opt && let None = Some(1) {} - //[disallowed]~^ ERROR leading irrefutable pattern in let chain if let Some(ref first) = opt && let Range { start: local_start, end: _ } = first @@ -43,7 +32,6 @@ fn main() { match opt { Some(ref first) if let second = first && let _third = second && let v = 4 + 4 => {}, - //[disallowed]~^ ERROR irrefutable `if let` guard patterns _ => {} } @@ -58,7 +46,6 @@ fn main() { 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 _ => {} } @@ -67,15 +54,13 @@ fn main() { while let first = &opt && let Some(second) = first && let None = second.start {} while let first = &opt && let (a, b) = (1, 2) {} - //[disallowed]~^ ERROR irrefutable `while let` patterns while let Some(ref first) = opt && let second = first && let _third = second {} - //[disallowed]~^ ERROR trailing irrefutable patterns in let chain while let Some(ref first) = opt && let Range { start: local_start, end: _ } = first - && let None = local_start { - } + && let None = local_start + {} // No error. An extra nesting level would be required for the `else if`. if opt == Some(None..None) { @@ -86,13 +71,11 @@ fn main() { if opt == Some(None..None) { } else if opt.is_some() && let x = &opt - //[disallowed]~^ ERROR trailing irrefutable pattern in let chain {} if opt == Some(None..None) { } else { if let x = opt.clone().map(|_| 1) - //[disallowed]~^ ERROR leading irrefutable pattern in let chain && x == Some(1) {} }