let_and_return: lint more cases in edition ≥ 2024

This commit is contained in:
Samuel Tardieu 2025-02-09 18:06:11 +01:00
parent 657dda7b50
commit 5d2fe079ab
7 changed files with 923 additions and 6 deletions

View file

@ -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
}
}
}

View file

@ -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()

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}