about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-02-09 18:06:11 +0100
committerSamuel Tardieu <sam@rfc1149.net>2025-02-09 19:03:45 +0100
commit5d2fe079ab8cbc8aaea26932d978d76b09b24f3f (patch)
tree5ce6ef1fd4c0f491429868d7af605bad3cdc08d9
parent657dda7b5091001a22722b4eeacdeddfe62b3b54 (diff)
downloadrust-5d2fe079ab8cbc8aaea26932d978d76b09b24f3f.tar.gz
rust-5d2fe079ab8cbc8aaea26932d978d76b09b24f3f.zip
`let_and_return`: lint more cases in edition ≥ 2024
-rw-r--r--clippy_lints/src/matches/significant_drop_in_scrutinee.rs6
-rw-r--r--clippy_lints/src/returns.rs3
-rw-r--r--tests/ui/let_and_return.edition2021.fixed265
-rw-r--r--tests/ui/let_and_return.edition2021.stderr152
-rw-r--r--tests/ui/let_and_return.edition2024.fixed265
-rw-r--r--tests/ui/let_and_return.edition2024.stderr228
-rw-r--r--tests/ui/let_and_return.rs10
7 files changed, 923 insertions, 6 deletions
diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 35f2e780d2e..37bac561a6e 100644
--- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -199,7 +199,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
             return false;
         }
 
-        let result = match ty.kind() {
+        match ty.kind() {
             rustc_middle::ty::Adt(adt, args) => {
                 // if some field has significant drop,
                 adt.all_fields()
@@ -223,9 +223,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
             rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)),
             rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty),
             _ => false,
-        };
-
-        result
+        }
     }
 }
 
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 3fcab6a766b..0286c9843a9 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -21,6 +21,7 @@ use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, GenericArgKind, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
+use rustc_span::edition::Edition;
 use rustc_span::{BytePos, Pos, Span, sym};
 use std::borrow::Cow;
 use std::fmt::Display;
@@ -235,7 +236,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             && let Some(initexpr) = &local.init
             && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
             && path_to_local_id(retexpr, local_id)
-            && !last_statement_borrows(cx, initexpr)
+            && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr))
             && !initexpr.span.in_external_macro(cx.sess().source_map())
             && !retexpr.span.in_external_macro(cx.sess().source_map())
             && !local.span.from_expansion()
diff --git a/tests/ui/let_and_return.edition2021.fixed b/tests/ui/let_and_return.edition2021.fixed
new file mode 100644
index 00000000000..c160d5df30f
--- /dev/null
+++ b/tests/ui/let_and_return.edition2021.fixed
@@ -0,0 +1,265 @@
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+
+#![allow(unused)]
+#![warn(clippy::let_and_return)]
+
+use std::cell::RefCell;
+
+fn test() -> i32 {
+    let _y = 0; // no warning
+    
+    5
+    //~^ ERROR: returning the result of a `let` binding from a block
+    //~| NOTE: `-D clippy::let-and-return` implied by `-D warnings`
+}
+
+fn test_inner() -> i32 {
+    if true {
+        
+        5
+        //~^ ERROR: returning the result of a `let` binding from a block
+    } else {
+        0
+    }
+}
+
+fn test_nowarn_1() -> i32 {
+    let mut x = 5;
+    x += 1;
+    x
+}
+
+fn test_nowarn_2() -> i32 {
+    let x = 5;
+    x + 1
+}
+
+fn test_nowarn_3() -> (i32, i32) {
+    // this should technically warn, but we do not compare complex patterns
+    let (x, y) = (5, 9);
+    (x, y)
+}
+
+fn test_nowarn_4() -> i32 {
+    // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type
+    let x: i32 = 5;
+    x
+}
+
+fn test_nowarn_5(x: i16) -> u16 {
+    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
+    let x = x as u16;
+    x
+}
+
+// False positive example
+trait Decode {
+    fn decode<D: std::io::Read>(d: D) -> Result<Self, ()>
+    where
+        Self: Sized;
+}
+
+macro_rules! tuple_encode {
+    ($($x:ident),*) => (
+        impl<$($x: Decode),*> Decode for ($($x),*) {
+            #[inline]
+            #[allow(non_snake_case)]
+            fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> {
+                // Shouldn't trigger lint
+                Ok(($({let $x = Decode::decode(&mut d)?; $x }),*))
+            }
+        }
+    );
+}
+
+fn issue_3792() -> String {
+    use std::io::{self, BufRead, Stdin};
+
+    let stdin = io::stdin();
+    // `Stdin::lock` returns `StdinLock<'static>` so `line` doesn't borrow from `stdin`
+    // https://github.com/rust-lang/rust/pull/93965
+    
+    stdin.lock().lines().next().unwrap().unwrap()
+    //~^ ERROR: returning the result of a `let` binding from a block
+}
+
+tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
+
+mod no_lint_if_stmt_borrows {
+    use std::cell::RefCell;
+    use std::rc::{Rc, Weak};
+    struct Bar;
+
+    impl Bar {
+        fn new() -> Self {
+            Bar {}
+        }
+        fn baz(&self) -> u32 {
+            0
+        }
+    }
+
+    fn issue_3324(value: Weak<RefCell<Bar>>) -> u32 {
+        let value = value.upgrade().unwrap();
+        let ret = value.borrow().baz();
+        ret
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn borrows_in_closure(value: Weak<RefCell<Bar>>) -> u32 {
+        fn f(mut x: impl FnMut() -> u32) -> impl FnMut() -> u32 {
+            x
+        }
+
+        let value = value.upgrade().unwrap();
+        let ret = f(|| value.borrow().baz())();
+        ret
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    mod free_function {
+        struct Inner;
+
+        struct Foo<'a> {
+            inner: &'a Inner,
+        }
+
+        impl Drop for Foo<'_> {
+            fn drop(&mut self) {}
+        }
+
+        impl<'a> Foo<'a> {
+            fn new(inner: &'a Inner) -> Self {
+                Self { inner }
+            }
+
+            fn value(&self) -> i32 {
+                42
+            }
+        }
+
+        fn some_foo(inner: &Inner) -> Foo<'_> {
+            Foo { inner }
+        }
+
+        fn test() -> i32 {
+            let x = Inner {};
+            let value = some_foo(&x).value();
+            value
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+        }
+
+        fn test2() -> i32 {
+            let x = Inner {};
+            let value = Foo::new(&x).value();
+            value
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+mod issue_5729 {
+    use std::sync::Arc;
+
+    trait Foo {}
+
+    trait FooStorage {
+        fn foo_cloned(&self) -> Arc<dyn Foo>;
+    }
+
+    struct FooStorageImpl<T: Foo> {
+        foo: Arc<T>,
+    }
+
+    impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
+        fn foo_cloned(&self) -> Arc<dyn Foo> {
+            
+            (Arc::clone(&self.foo)) as _
+            //~^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+mod issue_11335 {
+    pub enum E<T> {
+        A(T),
+        B(T),
+    }
+
+    impl<T> E<T> {
+        pub fn inner(&self) -> &T {
+            
+
+            (match self {
+                E::A(x) => x,
+                E::B(x) => x,
+            }) as _
+            //~^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/11167
+macro_rules! fn_in_macro {
+    ($b:block) => {
+        fn f() -> usize $b
+    }
+}
+fn_in_macro!({
+    return 1;
+});
+
+fn issue9150() -> usize {
+    let x = 1;
+    #[cfg(any())]
+    panic!("can't see me");
+    x
+}
+
+fn issue12801() {
+    fn left_is_if() -> String {
+        
+        (if true { "a".to_string() } else { "b".to_string() } + "c")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn no_par_needed() -> String {
+        
+        "c".to_string() + if true { "a" } else { "b" }
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn conjunctive_blocks() -> String {
+        
+        ({ "a".to_string() } + "b" + { "c" } + "d")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    #[allow(clippy::overly_complex_bool_expr)]
+    fn other_ops() {
+        let _ = || {
+            
+            (if true { 2 } else { 3 } << 4)
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+        let _ = || {
+            
+            ({ true } || { false } && { 2 <= 3 })
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+    }
+}
+
+fn issue14164() -> Result<u32, ()> {
+    let v = std::cell::RefCell::new(Some(vec![1]));
+    let r = match &*v.borrow() {
+        Some(v) => Ok(Ok(v[0])),
+        None => Ok(Ok(0)),
+    }?;
+    r
+    //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+}
+
+fn main() {}
diff --git a/tests/ui/let_and_return.edition2021.stderr b/tests/ui/let_and_return.edition2021.stderr
new file mode 100644
index 00000000000..105fa7a722d
--- /dev/null
+++ b/tests/ui/let_and_return.edition2021.stderr
@@ -0,0 +1,152 @@
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:13:5
+   |
+LL |     let x = 5;
+   |     ---------- unnecessary `let` binding
+LL |     x
+   |     ^
+   |
+   = note: `-D clippy::let-and-return` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]`
+help: return the expression directly
+   |
+LL ~     
+LL ~     5
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:21:9
+   |
+LL |         let x = 5;
+   |         ---------- unnecessary `let` binding
+LL |         x
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         5
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:84:5
+   |
+LL |     let line = stdin.lock().lines().next().unwrap().unwrap();
+   |     --------------------------------------------------------- unnecessary `let` binding
+LL |     line
+   |     ^^^^
+   |
+help: return the expression directly
+   |
+LL ~     
+LL ~     stdin.lock().lines().next().unwrap().unwrap()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:179:13
+   |
+LL |             let clone = Arc::clone(&self.foo);
+   |             ---------------------------------- unnecessary `let` binding
+LL |             clone
+   |             ^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             (Arc::clone(&self.foo)) as _
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:198:13
+   |
+LL | /             let result = match self {
+LL | |                 E::A(x) => x,
+LL | |                 E::B(x) => x,
+LL | |             };
+   | |______________- unnecessary `let` binding
+LL |
+LL |               result
+   |               ^^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL |
+LL ~             (match self {
+LL +                 E::A(x) => x,
+LL +                 E::B(x) => x,
+LL +             }) as _
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:224:9
+   |
+LL |         let s = if true { "a".to_string() } else { "b".to_string() } + "c";
+   |         ------------------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         (if true { "a".to_string() } else { "b".to_string() } + "c")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:230:9
+   |
+LL |         let s = "c".to_string() + if true { "a" } else { "b" };
+   |         ------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         "c".to_string() + if true { "a" } else { "b" }
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:236:9
+   |
+LL |         let s = { "a".to_string() } + "b" + { "c" } + "d";
+   |         -------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         ({ "a".to_string() } + "b" + { "c" } + "d")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:244:13
+   |
+LL |             let s = if true { 2 } else { 3 } << 4;
+   |             -------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             (if true { 2 } else { 3 } << 4)
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:249:13
+   |
+LL |             let s = { true } || { false } && { 2 <= 3 };
+   |             -------------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             ({ true } || { false } && { 2 <= 3 })
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/let_and_return.edition2024.fixed b/tests/ui/let_and_return.edition2024.fixed
new file mode 100644
index 00000000000..f958f70e1dc
--- /dev/null
+++ b/tests/ui/let_and_return.edition2024.fixed
@@ -0,0 +1,265 @@
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+
+#![allow(unused)]
+#![warn(clippy::let_and_return)]
+
+use std::cell::RefCell;
+
+fn test() -> i32 {
+    let _y = 0; // no warning
+    
+    5
+    //~^ ERROR: returning the result of a `let` binding from a block
+    //~| NOTE: `-D clippy::let-and-return` implied by `-D warnings`
+}
+
+fn test_inner() -> i32 {
+    if true {
+        
+        5
+        //~^ ERROR: returning the result of a `let` binding from a block
+    } else {
+        0
+    }
+}
+
+fn test_nowarn_1() -> i32 {
+    let mut x = 5;
+    x += 1;
+    x
+}
+
+fn test_nowarn_2() -> i32 {
+    let x = 5;
+    x + 1
+}
+
+fn test_nowarn_3() -> (i32, i32) {
+    // this should technically warn, but we do not compare complex patterns
+    let (x, y) = (5, 9);
+    (x, y)
+}
+
+fn test_nowarn_4() -> i32 {
+    // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type
+    let x: i32 = 5;
+    x
+}
+
+fn test_nowarn_5(x: i16) -> u16 {
+    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
+    let x = x as u16;
+    x
+}
+
+// False positive example
+trait Decode {
+    fn decode<D: std::io::Read>(d: D) -> Result<Self, ()>
+    where
+        Self: Sized;
+}
+
+macro_rules! tuple_encode {
+    ($($x:ident),*) => (
+        impl<$($x: Decode),*> Decode for ($($x),*) {
+            #[inline]
+            #[allow(non_snake_case)]
+            fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> {
+                // Shouldn't trigger lint
+                Ok(($({let $x = Decode::decode(&mut d)?; $x }),*))
+            }
+        }
+    );
+}
+
+fn issue_3792() -> String {
+    use std::io::{self, BufRead, Stdin};
+
+    let stdin = io::stdin();
+    // `Stdin::lock` returns `StdinLock<'static>` so `line` doesn't borrow from `stdin`
+    // https://github.com/rust-lang/rust/pull/93965
+    
+    stdin.lock().lines().next().unwrap().unwrap()
+    //~^ ERROR: returning the result of a `let` binding from a block
+}
+
+tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
+
+mod no_lint_if_stmt_borrows {
+    use std::cell::RefCell;
+    use std::rc::{Rc, Weak};
+    struct Bar;
+
+    impl Bar {
+        fn new() -> Self {
+            Bar {}
+        }
+        fn baz(&self) -> u32 {
+            0
+        }
+    }
+
+    fn issue_3324(value: Weak<RefCell<Bar>>) -> u32 {
+        let value = value.upgrade().unwrap();
+        
+        value.borrow().baz()
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn borrows_in_closure(value: Weak<RefCell<Bar>>) -> u32 {
+        fn f(mut x: impl FnMut() -> u32) -> impl FnMut() -> u32 {
+            x
+        }
+
+        let value = value.upgrade().unwrap();
+        
+        f(|| value.borrow().baz())()
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    mod free_function {
+        struct Inner;
+
+        struct Foo<'a> {
+            inner: &'a Inner,
+        }
+
+        impl Drop for Foo<'_> {
+            fn drop(&mut self) {}
+        }
+
+        impl<'a> Foo<'a> {
+            fn new(inner: &'a Inner) -> Self {
+                Self { inner }
+            }
+
+            fn value(&self) -> i32 {
+                42
+            }
+        }
+
+        fn some_foo(inner: &Inner) -> Foo<'_> {
+            Foo { inner }
+        }
+
+        fn test() -> i32 {
+            let x = Inner {};
+            
+            some_foo(&x).value()
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+        }
+
+        fn test2() -> i32 {
+            let x = Inner {};
+            
+            Foo::new(&x).value()
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+mod issue_5729 {
+    use std::sync::Arc;
+
+    trait Foo {}
+
+    trait FooStorage {
+        fn foo_cloned(&self) -> Arc<dyn Foo>;
+    }
+
+    struct FooStorageImpl<T: Foo> {
+        foo: Arc<T>,
+    }
+
+    impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
+        fn foo_cloned(&self) -> Arc<dyn Foo> {
+            
+            (Arc::clone(&self.foo)) as _
+            //~^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+mod issue_11335 {
+    pub enum E<T> {
+        A(T),
+        B(T),
+    }
+
+    impl<T> E<T> {
+        pub fn inner(&self) -> &T {
+            
+
+            (match self {
+                E::A(x) => x,
+                E::B(x) => x,
+            }) as _
+            //~^ ERROR: returning the result of a `let` binding from a block
+        }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/11167
+macro_rules! fn_in_macro {
+    ($b:block) => {
+        fn f() -> usize $b
+    }
+}
+fn_in_macro!({
+    return 1;
+});
+
+fn issue9150() -> usize {
+    let x = 1;
+    #[cfg(any())]
+    panic!("can't see me");
+    x
+}
+
+fn issue12801() {
+    fn left_is_if() -> String {
+        
+        (if true { "a".to_string() } else { "b".to_string() } + "c")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn no_par_needed() -> String {
+        
+        "c".to_string() + if true { "a" } else { "b" }
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn conjunctive_blocks() -> String {
+        
+        ({ "a".to_string() } + "b" + { "c" } + "d")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    #[allow(clippy::overly_complex_bool_expr)]
+    fn other_ops() {
+        let _ = || {
+            
+            (if true { 2 } else { 3 } << 4)
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+        let _ = || {
+            
+            ({ true } || { false } && { 2 <= 3 })
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+    }
+}
+
+fn issue14164() -> Result<u32, ()> {
+    let v = std::cell::RefCell::new(Some(vec![1]));
+    
+    match &*v.borrow() {
+        Some(v) => Ok(Ok(v[0])),
+        None => Ok(Ok(0)),
+    }?
+    //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
+}
+
+fn main() {}
diff --git a/tests/ui/let_and_return.edition2024.stderr b/tests/ui/let_and_return.edition2024.stderr
new file mode 100644
index 00000000000..ec87e32b582
--- /dev/null
+++ b/tests/ui/let_and_return.edition2024.stderr
@@ -0,0 +1,228 @@
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:13:5
+   |
+LL |     let x = 5;
+   |     ---------- unnecessary `let` binding
+LL |     x
+   |     ^
+   |
+   = note: `-D clippy::let-and-return` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]`
+help: return the expression directly
+   |
+LL ~     
+LL ~     5
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:21:9
+   |
+LL |         let x = 5;
+   |         ---------- unnecessary `let` binding
+LL |         x
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         5
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:84:5
+   |
+LL |     let line = stdin.lock().lines().next().unwrap().unwrap();
+   |     --------------------------------------------------------- unnecessary `let` binding
+LL |     line
+   |     ^^^^
+   |
+help: return the expression directly
+   |
+LL ~     
+LL ~     stdin.lock().lines().next().unwrap().unwrap()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:107:9
+   |
+LL |         let ret = value.borrow().baz();
+   |         ------------------------------- unnecessary `let` binding
+LL |         ret
+   |         ^^^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         value.borrow().baz()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:118:9
+   |
+LL |         let ret = f(|| value.borrow().baz())();
+   |         --------------------------------------- unnecessary `let` binding
+LL |         ret
+   |         ^^^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         f(|| value.borrow().baz())()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:150:13
+   |
+LL |             let value = some_foo(&x).value();
+   |             --------------------------------- unnecessary `let` binding
+LL |             value
+   |             ^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             some_foo(&x).value()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:157:13
+   |
+LL |             let value = Foo::new(&x).value();
+   |             --------------------------------- unnecessary `let` binding
+LL |             value
+   |             ^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             Foo::new(&x).value()
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:179:13
+   |
+LL |             let clone = Arc::clone(&self.foo);
+   |             ---------------------------------- unnecessary `let` binding
+LL |             clone
+   |             ^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             (Arc::clone(&self.foo)) as _
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:198:13
+   |
+LL | /             let result = match self {
+LL | |                 E::A(x) => x,
+LL | |                 E::B(x) => x,
+LL | |             };
+   | |______________- unnecessary `let` binding
+LL |
+LL |               result
+   |               ^^^^^^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL |
+LL ~             (match self {
+LL +                 E::A(x) => x,
+LL +                 E::B(x) => x,
+LL +             }) as _
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:224:9
+   |
+LL |         let s = if true { "a".to_string() } else { "b".to_string() } + "c";
+   |         ------------------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         (if true { "a".to_string() } else { "b".to_string() } + "c")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:230:9
+   |
+LL |         let s = "c".to_string() + if true { "a" } else { "b" };
+   |         ------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         "c".to_string() + if true { "a" } else { "b" }
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:236:9
+   |
+LL |         let s = { "a".to_string() } + "b" + { "c" } + "d";
+   |         -------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         ({ "a".to_string() } + "b" + { "c" } + "d")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:244:13
+   |
+LL |             let s = if true { 2 } else { 3 } << 4;
+   |             -------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             (if true { 2 } else { 3 } << 4)
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:249:13
+   |
+LL |             let s = { true } || { false } && { 2 <= 3 };
+   |             -------------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             ({ true } || { false } && { 2 <= 3 })
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:261:5
+   |
+LL | /     let r = match &*v.borrow() {
+LL | |         Some(v) => Ok(Ok(v[0])),
+LL | |         None => Ok(Ok(0)),
+LL | |     }?;
+   | |_______- unnecessary `let` binding
+LL |       r
+   |       ^
+   |
+help: return the expression directly
+   |
+LL ~     
+LL ~     match &*v.borrow() {
+LL +         Some(v) => Ok(Ok(v[0])),
+LL +         None => Ok(Ok(0)),
+LL +     }?
+   |
+
+error: aborting due to 15 previous errors
+
diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs
index f9da55946c0..0b7a1e26890 100644
--- a/tests/ui/let_and_return.rs
+++ b/tests/ui/let_and_return.rs
@@ -1,3 +1,7 @@
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+
 #![allow(unused)]
 #![warn(clippy::let_and_return)]
 
@@ -101,6 +105,7 @@ mod no_lint_if_stmt_borrows {
         let value = value.upgrade().unwrap();
         let ret = value.borrow().baz();
         ret
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
     }
 
     fn borrows_in_closure(value: Weak<RefCell<Bar>>) -> u32 {
@@ -111,6 +116,7 @@ mod no_lint_if_stmt_borrows {
         let value = value.upgrade().unwrap();
         let ret = f(|| value.borrow().baz())();
         ret
+        //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
     }
 
     mod free_function {
@@ -142,12 +148,14 @@ mod no_lint_if_stmt_borrows {
             let x = Inner {};
             let value = some_foo(&x).value();
             value
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
         }
 
         fn test2() -> i32 {
             let x = Inner {};
             let value = Foo::new(&x).value();
             value
+            //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
         }
     }
 }
@@ -244,7 +252,6 @@ fn issue12801() {
     }
 }
 
-// Do not lint
 fn issue14164() -> Result<u32, ()> {
     let v = std::cell::RefCell::new(Some(vec![1]));
     let r = match &*v.borrow() {
@@ -252,6 +259,7 @@ fn issue14164() -> Result<u32, ()> {
         None => Ok(Ok(0)),
     }?;
     r
+    //~[edition2024]^ ERROR: returning the result of a `let` binding from a block
 }
 
 fn main() {}