about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/collapsible_if.rs17
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_utils/src/msrvs.rs1
-rw-r--r--tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed1
-rw-r--r--tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs1
-rw-r--r--tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr6
-rw-r--r--tests/ui/auxiliary/proc_macro_attr.rs79
-rw-r--r--tests/ui/auxiliary/proc_macros.rs1
-rw-r--r--tests/ui/collapsible_if.fixed19
-rw-r--r--tests/ui/collapsible_if.rs19
-rw-r--r--tests/ui/collapsible_if.stderr8
-rw-r--r--tests/ui/collapsible_if_let_chains.edition2024.fixed68
-rw-r--r--tests/ui/collapsible_if_let_chains.edition2024.stderr132
-rw-r--r--tests/ui/collapsible_if_let_chains.fixed29
-rw-r--r--tests/ui/collapsible_if_let_chains.rs52
-rw-r--r--tests/ui/collapsible_if_let_chains.stderr58
-rw-r--r--tests/ui/collapsible_match.rs1
-rw-r--r--tests/ui/collapsible_match.stderr52
-rw-r--r--tests/ui/index_refutable_slice/if_let_slice_binding.fixed2
-rw-r--r--tests/ui/index_refutable_slice/if_let_slice_binding.rs2
-rw-r--r--tests/ui/needless_late_init.fixed2
-rw-r--r--tests/ui/needless_late_init.rs2
-rw-r--r--tests/ui/needless_late_init.stderr34
23 files changed, 351 insertions, 237 deletions
diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs
index 20fae8a6775..b231f05ed88 100644
--- a/clippy_lints/src/collapsible_if.rs
+++ b/clippy_lints/src/collapsible_if.rs
@@ -1,11 +1,11 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability};
 use rustc_ast::BinOpKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
 
@@ -78,14 +78,14 @@ declare_clippy_lint! {
 }
 
 pub struct CollapsibleIf {
-    let_chains_enabled: bool,
+    msrv: Msrv,
     lint_commented_code: bool,
 }
 
 impl CollapsibleIf {
-    pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
+    pub fn new(conf: &'static Conf) -> Self {
         Self {
-            let_chains_enabled: tcx.features().let_chains(),
+            msrv: conf.msrv,
             lint_commented_code: conf.lint_commented_code,
         }
     }
@@ -127,7 +127,7 @@ impl CollapsibleIf {
         if let Some(inner) = expr_block(then)
             && cx.tcx.hir_attrs(inner.hir_id).is_empty()
             && let ExprKind::If(check_inner, _, None) = &inner.kind
-            && self.eligible_condition(check_inner)
+            && self.eligible_condition(cx, check_inner)
             && let ctxt = expr.span.ctxt()
             && inner.span.ctxt() == ctxt
             && (self.lint_commented_code || !block_starts_with_comment(cx, then))
@@ -163,8 +163,9 @@ impl CollapsibleIf {
         }
     }
 
-    pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool {
-        self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..))
+    fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool {
+        !matches!(cond.kind, ExprKind::Let(..))
+            || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS))
     }
 }
 
@@ -180,7 +181,7 @@ impl LateLintPass<'_> for CollapsibleIf {
             {
                 Self::check_collapsible_else_if(cx, then.span, else_);
             } else if else_.is_none()
-                && self.eligible_condition(cond)
+                && self.eligible_condition(cx, cond)
                 && let ExprKind::Block(then, None) = then.kind
             {
                 self.check_collapsible_if_if(cx, expr, cond, then);
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index bc7fc60827a..62e2b565b48 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -731,7 +731,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
     store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit));
     store.register_late_pass(|_| Box::new(returns::Return));
-    store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf)));
+    store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf)));
     store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
     store.register_early_pass(|| Box::new(precedence::Precedence));
     store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 19061b574ff..71985cb4b09 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -22,6 +22,7 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
+    1,88,0 { LET_CHAINS }
     1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT }
     1,85,0 { UINT_FLOAT_MIDPOINT }
     1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed
index f12273954c6..5e189471b00 100644
--- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed
+++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed
@@ -1,4 +1,3 @@
-#![feature(let_chains)]
 #![warn(clippy::collapsible_if)]
 
 fn main() {
diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs
index 5a984d7a3cb..525eebf632a 100644
--- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs
+++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs
@@ -1,4 +1,3 @@
-#![feature(let_chains)]
 #![warn(clippy::collapsible_if)]
 
 fn main() {
diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr
index c22a65a4473..c9de166a969 100644
--- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr
+++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr
@@ -1,5 +1,5 @@
 error: this `if` statement can be collapsed
-  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5
+  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:4:5
    |
 LL | /     if let Some(a) = Some(3) {
 LL | |         // with comment
@@ -21,7 +21,7 @@ LL ~         }
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5
+  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:12:5
    |
 LL | /     if let Some(a) = Some(3) {
 LL | |         // with comment
@@ -41,7 +41,7 @@ LL ~         }
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5
+  --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:20:5
    |
 LL | /     if Some(3) == Some(4).map(|x| x - 1) {
 LL | |         // with comment
diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs
index e72d6b6cead..4c61c5accd3 100644
--- a/tests/ui/auxiliary/proc_macro_attr.rs
+++ b/tests/ui/auxiliary/proc_macro_attr.rs
@@ -51,14 +51,14 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea
 
     fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
         let arg = sig.inputs.first_mut()?;
-        if let FnArg::Typed(PatType { pat, .. }) = arg {
-            if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
-                if ident == "self" {
-                    return Some(arg);
-                }
-            }
+        if let FnArg::Typed(PatType { pat, .. }) = arg
+            && let Pat::Ident(PatIdent { ident, .. }) = &**pat
+            && ident == "self"
+        {
+            Some(arg)
+        } else {
+            None
         }
-        None
     }
 
     let mut elided = 0;
@@ -66,30 +66,29 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea
 
     // Look for methods having arbitrary self type taken by &mut ref
     for inner in &mut item.items {
-        if let ImplItem::Fn(method) = inner {
-            if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
-                if let box Type::Reference(reference) = &mut pat_type.ty {
-                    // Target only unnamed lifetimes
-                    let name = match &reference.lifetime {
-                        Some(lt) if lt.ident == "_" => make_name(elided),
-                        None => make_name(elided),
-                        _ => continue,
-                    };
-                    elided += 1;
-
-                    // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
-                    // In order to avoid adding the dependency, get a default span from a nonexistent token.
-                    // A default span is needed to mark the code as coming from expansion.
-                    let span = Star::default().span();
-
-                    // Replace old lifetime with the named one
-                    let lifetime = Lifetime::new(&name, span);
-                    reference.lifetime = Some(parse_quote!(#lifetime));
-
-                    // Add lifetime to the generics of the method
-                    method.sig.generics.params.push(parse_quote!(#lifetime));
-                }
-            }
+        if let ImplItem::Fn(method) = inner
+            && let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig)
+            && let box Type::Reference(reference) = &mut pat_type.ty
+        {
+            // Target only unnamed lifetimes
+            let name = match &reference.lifetime {
+                Some(lt) if lt.ident == "_" => make_name(elided),
+                None => make_name(elided),
+                _ => continue,
+            };
+            elided += 1;
+
+            // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
+            // In order to avoid adding the dependency, get a default span from a nonexistent token.
+            // A default span is needed to mark the code as coming from expansion.
+            let span = Star::default().span();
+
+            // Replace old lifetime with the named one
+            let lifetime = Lifetime::new(&name, span);
+            reference.lifetime = Some(parse_quote!(#lifetime));
+
+            // Add lifetime to the generics of the method
+            method.sig.generics.params.push(parse_quote!(#lifetime));
         }
     }
 
@@ -129,15 +128,15 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream
     let mut async_fn = parse_macro_input!(input as syn::ItemFn);
 
     for stmt in &mut async_fn.block.stmts {
-        if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt {
-            if let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() {
-                let blc = quote_spanned!( await_token.span => {
-                    #[allow(clippy::let_and_return)]
-                    let __pinned = #base;
-                    __pinned
-                });
-                *scrutinee = parse_quote!(#blc);
-            }
+        if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt
+            && let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut()
+        {
+            let blc = quote_spanned!( await_token.span => {
+                #[allow(clippy::let_and_return)]
+                let __pinned = #base;
+                __pinned
+            });
+            *scrutinee = parse_quote!(#blc);
         }
     }
 
diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs
index 7a4cc4fa9ee..bb55539617f 100644
--- a/tests/ui/auxiliary/proc_macros.rs
+++ b/tests/ui/auxiliary/proc_macros.rs
@@ -1,4 +1,3 @@
-#![feature(let_chains)]
 #![feature(proc_macro_span)]
 #![allow(clippy::needless_if, dead_code)]
 
diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed
index e1ceb04f9cb..d62309fe08b 100644
--- a/tests/ui/collapsible_if.fixed
+++ b/tests/ui/collapsible_if.fixed
@@ -101,27 +101,8 @@ fn main() {
         }
     }
 
-    // Test behavior wrt. `let_chains`.
-    // None of the cases below should be collapsed.
     fn truth() -> bool { true }
 
-    // Prefix:
-    if let 0 = 1 {
-        if truth() {}
-    }
-
-    // Suffix:
-    if truth() {
-        if let 0 = 1 {}
-    }
-
-    // Midfix:
-    if truth() {
-        if let 0 = 1 {
-            if truth() {}
-        }
-    }
-
     // Fix #5962
     if matches!(true, true)
         && matches!(true, true) {}
diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs
index 0b996dca22e..0677b98bb5b 100644
--- a/tests/ui/collapsible_if.rs
+++ b/tests/ui/collapsible_if.rs
@@ -108,27 +108,8 @@ fn main() {
         }
     }
 
-    // Test behavior wrt. `let_chains`.
-    // None of the cases below should be collapsed.
     fn truth() -> bool { true }
 
-    // Prefix:
-    if let 0 = 1 {
-        if truth() {}
-    }
-
-    // Suffix:
-    if truth() {
-        if let 0 = 1 {}
-    }
-
-    // Midfix:
-    if truth() {
-        if let 0 = 1 {
-            if truth() {}
-        }
-    }
-
     // Fix #5962
     if matches!(true, true) {
         if matches!(true, true) {}
diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr
index 53281146239..32c6b019403 100644
--- a/tests/ui/collapsible_if.stderr
+++ b/tests/ui/collapsible_if.stderr
@@ -127,7 +127,7 @@ LL ~         }
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if.rs:133:5
+  --> tests/ui/collapsible_if.rs:114:5
    |
 LL | /     if matches!(true, true) {
 LL | |         if matches!(true, true) {}
@@ -141,7 +141,7 @@ LL ~         && matches!(true, true) {}
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if.rs:139:5
+  --> tests/ui/collapsible_if.rs:120:5
    |
 LL | /     if matches!(true, true) && truth() {
 LL | |         if matches!(true, true) {}
@@ -155,7 +155,7 @@ LL ~         && matches!(true, true) {}
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if.rs:151:5
+  --> tests/ui/collapsible_if.rs:132:5
    |
 LL | /     if true {
 LL | |         if true {
@@ -173,7 +173,7 @@ LL ~         }
    |
 
 error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if.rs:168:5
+  --> tests/ui/collapsible_if.rs:149:5
    |
 LL | /     if true {
 LL | |         if true {
diff --git a/tests/ui/collapsible_if_let_chains.edition2024.fixed b/tests/ui/collapsible_if_let_chains.edition2024.fixed
new file mode 100644
index 00000000000..ad08c21ba9e
--- /dev/null
+++ b/tests/ui/collapsible_if_let_chains.edition2024.fixed
@@ -0,0 +1,68 @@
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+//@[edition2021] check-pass
+
+#![warn(clippy::collapsible_if)]
+
+fn main() {
+    if let Some(a) = Some(3) {
+        // with comment, so do not lint
+        if let Some(b) = Some(4) {
+            let _ = a + b;
+        }
+    }
+
+    //~[edition2024]v collapsible_if
+    if let Some(a) = Some(3)
+        && let Some(b) = Some(4) {
+            let _ = a + b;
+        }
+
+    //~[edition2024]v collapsible_if
+    if let Some(a) = Some(3)
+        && a + 1 == 4 {
+            let _ = a;
+        }
+
+    //~[edition2024]v collapsible_if
+    if Some(3) == Some(4).map(|x| x - 1)
+        && let Some(b) = Some(4) {
+            let _ = b;
+        }
+
+    fn truth() -> bool {
+        true
+    }
+
+    // Prefix:
+    //~[edition2024]v collapsible_if
+    if let 0 = 1
+        && truth() {}
+
+    // Suffix:
+    //~[edition2024]v collapsible_if
+    if truth()
+        && let 0 = 1 {}
+
+    // Midfix:
+    //~[edition2024]vvv collapsible_if
+    //~[edition2024]v collapsible_if
+    if truth()
+        && let 0 = 1
+            && truth() {}
+}
+
+#[clippy::msrv = "1.87.0"]
+fn msrv_1_87() {
+    if let 0 = 1 {
+        if true {}
+    }
+}
+
+#[clippy::msrv = "1.88.0"]
+fn msrv_1_88() {
+    //~[edition2024]v collapsible_if
+    if let 0 = 1
+        && true {}
+}
diff --git a/tests/ui/collapsible_if_let_chains.edition2024.stderr b/tests/ui/collapsible_if_let_chains.edition2024.stderr
new file mode 100644
index 00000000000..b0aa3cecadb
--- /dev/null
+++ b/tests/ui/collapsible_if_let_chains.edition2024.stderr
@@ -0,0 +1,132 @@
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:17:5
+   |
+LL | /     if let Some(a) = Some(3) {
+LL | |         if let Some(b) = Some(4) {
+LL | |             let _ = a + b;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::collapsible-if` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]`
+help: collapse nested if block
+   |
+LL ~     if let Some(a) = Some(3)
+LL ~         && let Some(b) = Some(4) {
+LL |             let _ = a + b;
+LL ~         }
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:24:5
+   |
+LL | /     if let Some(a) = Some(3) {
+LL | |         if a + 1 == 4 {
+LL | |             let _ = a;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if let Some(a) = Some(3)
+LL ~         && a + 1 == 4 {
+LL |             let _ = a;
+LL ~         }
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:31:5
+   |
+LL | /     if Some(3) == Some(4).map(|x| x - 1) {
+LL | |         if let Some(b) = Some(4) {
+LL | |             let _ = b;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if Some(3) == Some(4).map(|x| x - 1)
+LL ~         && let Some(b) = Some(4) {
+LL |             let _ = b;
+LL ~         }
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:43:5
+   |
+LL | /     if let 0 = 1 {
+LL | |         if truth() {}
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if let 0 = 1
+LL ~         && truth() {}
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:49:5
+   |
+LL | /     if truth() {
+LL | |         if let 0 = 1 {}
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if truth()
+LL ~         && let 0 = 1 {}
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:56:5
+   |
+LL | /     if truth() {
+LL | |         if let 0 = 1 {
+LL | |             if truth() {}
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if truth()
+LL ~         && let 0 = 1 {
+LL |             if truth() {}
+LL ~         }
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:57:9
+   |
+LL | /         if let 0 = 1 {
+LL | |             if truth() {}
+LL | |         }
+   | |_________^
+   |
+help: collapse nested if block
+   |
+LL ~         if let 0 = 1
+LL ~             && truth() {}
+   |
+
+error: this `if` statement can be collapsed
+  --> tests/ui/collapsible_if_let_chains.rs:73:5
+   |
+LL | /     if let 0 = 1 {
+LL | |         if true {}
+LL | |     }
+   | |_____^
+   |
+help: collapse nested if block
+   |
+LL ~     if let 0 = 1
+LL ~         && true {}
+   |
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/collapsible_if_let_chains.fixed b/tests/ui/collapsible_if_let_chains.fixed
deleted file mode 100644
index 3dd9498a4c9..00000000000
--- a/tests/ui/collapsible_if_let_chains.fixed
+++ /dev/null
@@ -1,29 +0,0 @@
-#![feature(let_chains)]
-#![warn(clippy::collapsible_if)]
-
-fn main() {
-    if let Some(a) = Some(3) {
-        // with comment, so do not lint
-        if let Some(b) = Some(4) {
-            let _ = a + b;
-        }
-    }
-
-    if let Some(a) = Some(3)
-        && let Some(b) = Some(4) {
-            let _ = a + b;
-        }
-    //~^^^^^ collapsible_if
-
-    if let Some(a) = Some(3)
-        && a + 1 == 4 {
-            let _ = a;
-        }
-    //~^^^^^ collapsible_if
-
-    if Some(3) == Some(4).map(|x| x - 1)
-        && let Some(b) = Some(4) {
-            let _ = b;
-        }
-    //~^^^^^ collapsible_if
-}
diff --git a/tests/ui/collapsible_if_let_chains.rs b/tests/ui/collapsible_if_let_chains.rs
index 064b9a0be48..b2e88b1a556 100644
--- a/tests/ui/collapsible_if_let_chains.rs
+++ b/tests/ui/collapsible_if_let_chains.rs
@@ -1,4 +1,8 @@
-#![feature(let_chains)]
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+//@[edition2021] check-pass
+
 #![warn(clippy::collapsible_if)]
 
 fn main() {
@@ -9,24 +13,64 @@ fn main() {
         }
     }
 
+    //~[edition2024]v collapsible_if
     if let Some(a) = Some(3) {
         if let Some(b) = Some(4) {
             let _ = a + b;
         }
     }
-    //~^^^^^ collapsible_if
 
+    //~[edition2024]v collapsible_if
     if let Some(a) = Some(3) {
         if a + 1 == 4 {
             let _ = a;
         }
     }
-    //~^^^^^ collapsible_if
 
+    //~[edition2024]v collapsible_if
     if Some(3) == Some(4).map(|x| x - 1) {
         if let Some(b) = Some(4) {
             let _ = b;
         }
     }
-    //~^^^^^ collapsible_if
+
+    fn truth() -> bool {
+        true
+    }
+
+    // Prefix:
+    //~[edition2024]v collapsible_if
+    if let 0 = 1 {
+        if truth() {}
+    }
+
+    // Suffix:
+    //~[edition2024]v collapsible_if
+    if truth() {
+        if let 0 = 1 {}
+    }
+
+    // Midfix:
+    //~[edition2024]vvv collapsible_if
+    //~[edition2024]v collapsible_if
+    if truth() {
+        if let 0 = 1 {
+            if truth() {}
+        }
+    }
+}
+
+#[clippy::msrv = "1.87.0"]
+fn msrv_1_87() {
+    if let 0 = 1 {
+        if true {}
+    }
+}
+
+#[clippy::msrv = "1.88.0"]
+fn msrv_1_88() {
+    //~[edition2024]v collapsible_if
+    if let 0 = 1 {
+        if true {}
+    }
 }
diff --git a/tests/ui/collapsible_if_let_chains.stderr b/tests/ui/collapsible_if_let_chains.stderr
deleted file mode 100644
index 64a88114c47..00000000000
--- a/tests/ui/collapsible_if_let_chains.stderr
+++ /dev/null
@@ -1,58 +0,0 @@
-error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if_let_chains.rs:12:5
-   |
-LL | /     if let Some(a) = Some(3) {
-LL | |         if let Some(b) = Some(4) {
-LL | |             let _ = a + b;
-LL | |         }
-LL | |     }
-   | |_____^
-   |
-   = note: `-D clippy::collapsible-if` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]`
-help: collapse nested if block
-   |
-LL ~     if let Some(a) = Some(3)
-LL ~         && let Some(b) = Some(4) {
-LL |             let _ = a + b;
-LL ~         }
-   |
-
-error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if_let_chains.rs:19:5
-   |
-LL | /     if let Some(a) = Some(3) {
-LL | |         if a + 1 == 4 {
-LL | |             let _ = a;
-LL | |         }
-LL | |     }
-   | |_____^
-   |
-help: collapse nested if block
-   |
-LL ~     if let Some(a) = Some(3)
-LL ~         && a + 1 == 4 {
-LL |             let _ = a;
-LL ~         }
-   |
-
-error: this `if` statement can be collapsed
-  --> tests/ui/collapsible_if_let_chains.rs:26:5
-   |
-LL | /     if Some(3) == Some(4).map(|x| x - 1) {
-LL | |         if let Some(b) = Some(4) {
-LL | |             let _ = b;
-LL | |         }
-LL | |     }
-   | |_____^
-   |
-help: collapse nested if block
-   |
-LL ~     if Some(3) == Some(4).map(|x| x - 1)
-LL ~         && let Some(b) = Some(4) {
-LL |             let _ = b;
-LL ~         }
-   |
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs
index 55ef5584495..71b82040ff6 100644
--- a/tests/ui/collapsible_match.rs
+++ b/tests/ui/collapsible_match.rs
@@ -1,5 +1,6 @@
 #![warn(clippy::collapsible_match)]
 #![allow(
+    clippy::collapsible_if,
     clippy::equatable_if_let,
     clippy::needless_return,
     clippy::no_effect,
diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr
index 5294a9d6975..c290d84ec29 100644
--- a/tests/ui/collapsible_match.stderr
+++ b/tests/ui/collapsible_match.stderr
@@ -1,5 +1,5 @@
 error: this `match` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:14:20
+  --> tests/ui/collapsible_match.rs:15:20
    |
 LL |           Ok(val) => match val {
    |  ____________________^
@@ -10,7 +10,7 @@ LL | |         },
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:14:12
+  --> tests/ui/collapsible_match.rs:15:12
    |
 LL |         Ok(val) => match val {
    |            ^^^ replace this binding
@@ -21,7 +21,7 @@ LL |             Some(n) => foo(n),
    = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]`
 
 error: this `match` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:24:20
+  --> tests/ui/collapsible_match.rs:25:20
    |
 LL |           Ok(val) => match val {
    |  ____________________^
@@ -32,7 +32,7 @@ LL | |         },
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:24:12
+  --> tests/ui/collapsible_match.rs:25:12
    |
 LL |         Ok(val) => match val {
    |            ^^^ replace this binding
@@ -41,7 +41,7 @@ LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
 error: this `if let` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:34:9
+  --> tests/ui/collapsible_match.rs:35:9
    |
 LL | /         if let Some(n) = val {
 LL | |
@@ -51,7 +51,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:33:15
+  --> tests/ui/collapsible_match.rs:34:15
    |
 LL |     if let Ok(val) = res_opt {
    |               ^^^ replace this binding
@@ -59,7 +59,7 @@ LL |         if let Some(n) = val {
    |                ^^^^^^^ with this pattern
 
 error: this `if let` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:43:9
+  --> tests/ui/collapsible_match.rs:44:9
    |
 LL | /         if let Some(n) = val {
 LL | |
@@ -71,7 +71,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:42:15
+  --> tests/ui/collapsible_match.rs:43:15
    |
 LL |     if let Ok(val) = res_opt {
    |               ^^^ replace this binding
@@ -79,7 +79,7 @@ LL |         if let Some(n) = val {
    |                ^^^^^^^ with this pattern
 
 error: this `match` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:56:9
+  --> tests/ui/collapsible_match.rs:57:9
    |
 LL | /         match val {
 LL | |
@@ -89,7 +89,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:55:15
+  --> tests/ui/collapsible_match.rs:56:15
    |
 LL |     if let Ok(val) = res_opt {
    |               ^^^ replace this binding
@@ -98,7 +98,7 @@ LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
 error: this `if let` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:66:13
+  --> tests/ui/collapsible_match.rs:67:13
    |
 LL | /             if let Some(n) = val {
 LL | |
@@ -108,7 +108,7 @@ LL | |             }
    | |_____________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:65:12
+  --> tests/ui/collapsible_match.rs:66:12
    |
 LL |         Ok(val) => {
    |            ^^^ replace this binding
@@ -116,7 +116,7 @@ LL |             if let Some(n) = val {
    |                    ^^^^^^^ with this pattern
 
 error: this `match` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:77:9
+  --> tests/ui/collapsible_match.rs:78:9
    |
 LL | /         match val {
 LL | |
@@ -126,7 +126,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:76:15
+  --> tests/ui/collapsible_match.rs:77:15
    |
 LL |     if let Ok(val) = res_opt {
    |               ^^^ replace this binding
@@ -135,7 +135,7 @@ LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
 error: this `if let` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:89:13
+  --> tests/ui/collapsible_match.rs:90:13
    |
 LL | /             if let Some(n) = val {
 LL | |
@@ -147,7 +147,7 @@ LL | |             }
    | |_____________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:88:12
+  --> tests/ui/collapsible_match.rs:89:12
    |
 LL |         Ok(val) => {
    |            ^^^ replace this binding
@@ -155,7 +155,7 @@ LL |             if let Some(n) = val {
    |                    ^^^^^^^ with this pattern
 
 error: this `match` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:102:20
+  --> tests/ui/collapsible_match.rs:103:20
    |
 LL |           Ok(val) => match val {
    |  ____________________^
@@ -166,7 +166,7 @@ LL | |         },
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:102:12
+  --> tests/ui/collapsible_match.rs:103:12
    |
 LL |         Ok(val) => match val {
    |            ^^^ replace this binding
@@ -175,7 +175,7 @@ LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
 error: this `match` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:112:22
+  --> tests/ui/collapsible_match.rs:113:22
    |
 LL |           Some(val) => match val {
    |  ______________________^
@@ -186,7 +186,7 @@ LL | |         },
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:112:14
+  --> tests/ui/collapsible_match.rs:113:14
    |
 LL |         Some(val) => match val {
    |              ^^^ replace this binding
@@ -195,7 +195,7 @@ LL |             Some(n) => foo(n),
    |             ^^^^^^^ with this pattern
 
 error: this `match` can be collapsed into the outer `match`
-  --> tests/ui/collapsible_match.rs:256:22
+  --> tests/ui/collapsible_match.rs:257:22
    |
 LL |           Some(val) => match val {
    |  ______________________^
@@ -206,7 +206,7 @@ LL | |         },
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:256:14
+  --> tests/ui/collapsible_match.rs:257:14
    |
 LL |         Some(val) => match val {
    |              ^^^ replace this binding
@@ -215,7 +215,7 @@ LL |             E::A(val) | E::B(val) => foo(val),
    |             ^^^^^^^^^^^^^^^^^^^^^ with this pattern
 
 error: this `if let` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:288:9
+  --> tests/ui/collapsible_match.rs:289:9
    |
 LL | /         if let Some(u) = a {
 LL | |
@@ -225,7 +225,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:287:27
+  --> tests/ui/collapsible_match.rs:288:27
    |
 LL |     if let Issue9647::A { a, .. } = x {
    |                           ^ replace this binding
@@ -233,7 +233,7 @@ LL |         if let Some(u) = a {
    |                ^^^^^^^ with this pattern, prefixed by `a`:
 
 error: this `if let` can be collapsed into the outer `if let`
-  --> tests/ui/collapsible_match.rs:298:9
+  --> tests/ui/collapsible_match.rs:299:9
    |
 LL | /         if let Some(u) = a {
 LL | |
@@ -243,7 +243,7 @@ LL | |         }
    | |_________^
    |
 help: the outer pattern can be modified to include the inner pattern
-  --> tests/ui/collapsible_match.rs:297:35
+  --> tests/ui/collapsible_match.rs:298:35
    |
 LL |     if let Issue9647::A { a: Some(a), .. } = x {
    |                                   ^ replace this binding
diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed
index e19aa4acb4c..050cdfcba96 100644
--- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed
+++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed
@@ -1,5 +1,5 @@
 #![deny(clippy::index_refutable_slice)]
-#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)]
+#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)]
 
 enum SomeEnum<T> {
     One(T),
diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs
index 29039356855..91429bfea27 100644
--- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs
+++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs
@@ -1,5 +1,5 @@
 #![deny(clippy::index_refutable_slice)]
-#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)]
+#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)]
 
 enum SomeEnum<T> {
     One(T),
diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed
index f832752ccd7..b686a8e9f1a 100644
--- a/tests/ui/needless_late_init.fixed
+++ b/tests/ui/needless_late_init.fixed
@@ -1,6 +1,4 @@
 //@aux-build:proc_macros.rs
-#![feature(let_chains)]
-#![allow(unused)]
 #![allow(
     clippy::assign_op_pattern,
     clippy::blocks_in_conditions,
diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs
index a52fbf52923..23772ff7029 100644
--- a/tests/ui/needless_late_init.rs
+++ b/tests/ui/needless_late_init.rs
@@ -1,6 +1,4 @@
 //@aux-build:proc_macros.rs
-#![feature(let_chains)]
-#![allow(unused)]
 #![allow(
     clippy::assign_op_pattern,
     clippy::blocks_in_conditions,
diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr
index b24c1275881..e3e25cdc8d7 100644
--- a/tests/ui/needless_late_init.stderr
+++ b/tests/ui/needless_late_init.stderr
@@ -1,5 +1,5 @@
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:27:5
+  --> tests/ui/needless_late_init.rs:25:5
    |
 LL |     let a;
    |     ^^^^^^ created here
@@ -17,7 +17,7 @@ LL ~     let a = "zero";
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:31:5
+  --> tests/ui/needless_late_init.rs:29:5
    |
 LL |     let b;
    |     ^^^^^^ created here
@@ -35,7 +35,7 @@ LL ~     let b = 1;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:33:5
+  --> tests/ui/needless_late_init.rs:31:5
    |
 LL |     let c;
    |     ^^^^^^ created here
@@ -52,7 +52,7 @@ LL ~     let c = 2;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:38:5
+  --> tests/ui/needless_late_init.rs:36:5
    |
 LL |     let d: usize;
    |     ^^^^^^^^^^^^^ created here
@@ -68,7 +68,7 @@ LL ~     let d: usize = 1;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:42:5
+  --> tests/ui/needless_late_init.rs:40:5
    |
 LL |     let e;
    |     ^^^^^^ created here
@@ -84,7 +84,7 @@ LL ~     let e = format!("{}", d);
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:48:5
+  --> tests/ui/needless_late_init.rs:46:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -103,7 +103,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:58:5
+  --> tests/ui/needless_late_init.rs:56:5
    |
 LL |     let b;
    |     ^^^^^^
@@ -120,7 +120,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:66:5
+  --> tests/ui/needless_late_init.rs:64:5
    |
 LL |     let d;
    |     ^^^^^^
@@ -138,7 +138,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:75:5
+  --> tests/ui/needless_late_init.rs:73:5
    |
 LL |     let e;
    |     ^^^^^^
@@ -155,7 +155,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:83:5
+  --> tests/ui/needless_late_init.rs:81:5
    |
 LL |     let f;
    |     ^^^^^^
@@ -169,7 +169,7 @@ LL ~         1 => "three",
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:90:5
+  --> tests/ui/needless_late_init.rs:88:5
    |
 LL |     let g: usize;
    |     ^^^^^^^^^^^^^
@@ -186,7 +186,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:99:5
+  --> tests/ui/needless_late_init.rs:97:5
    |
 LL |     let x;
    |     ^^^^^^ created here
@@ -203,7 +203,7 @@ LL ~     let x = 1;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:104:5
+  --> tests/ui/needless_late_init.rs:102:5
    |
 LL |     let x;
    |     ^^^^^^ created here
@@ -220,7 +220,7 @@ LL ~     let x = SignificantDrop;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:109:5
+  --> tests/ui/needless_late_init.rs:107:5
    |
 LL |     let x;
    |     ^^^^^^ created here
@@ -238,7 +238,7 @@ LL ~     let x = SignificantDrop;
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:129:5
+  --> tests/ui/needless_late_init.rs:127:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -257,7 +257,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:147:5
+  --> tests/ui/needless_late_init.rs:145:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -276,7 +276,7 @@ LL ~     };
    |
 
 error: unneeded late initialization
-  --> tests/ui/needless_late_init.rs:300:5
+  --> tests/ui/needless_late_init.rs:298:5
    |
 LL |     let r;
    |     ^^^^^^ created here