diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index c11da3147ef4..5f3474951d52 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -14,7 +14,7 @@ use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, StmtKind, }; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, GenericArgKind, Ty}; @@ -377,13 +377,27 @@ fn check_final_expr<'tcx>( } }; - if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { - return; - } let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if borrows { return; } + if ret_span.from_expansion() { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // However, this interacts very weirdly with `#[expect(clippy::needless_return)]` + // as adding that attribute will suppress the lint and then not fulfill the + // expectation. We'll therefore just manually mark the expectation. + match cx.tcx.lint_level_at_node(NEEDLESS_RETURN, expr.hir_id).0 { + Level::Allow => return, + Level::Expect(expectation) | Level::ForceWarn(Some(expectation)) => cx.fulfill_expectation(expectation), + _ => {}, + } + if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { + return; + } emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); }, @@ -415,10 +429,6 @@ fn emit_return_lint( replacement: &RetReplacement<'_>, at: HirId, ) { - if ret_span.from_expansion() { - return; - } - span_lint_hir_and_then( cx, NEEDLESS_RETURN, diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 853f685f04c7..a94dd7cc809e 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -234,6 +234,13 @@ fn issue_9361() -> i32 { return n + n; } +fn issue_12998() -> i32 { + let x = 1; + + #[expect(clippy::needless_return)] + return x; +} + fn issue8336(x: i32) -> bool { if x > 0 { println!("something"); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index e9c1e0e8ae8e..d70920c12a67 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -242,6 +242,13 @@ fn issue_9361() -> i32 { return n + n; } +fn issue_12998() -> i32 { + let x = 1; + + #[expect(clippy::needless_return)] + return x; +} + fn issue8336(x: i32) -> bool { if x > 0 { println!("something"); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 6c891fe7ad3f..c9cf52797d00 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -483,7 +483,7 @@ LL + format!("Hello {}", "world!") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:248:9 + --> tests/ui/needless_return.rs:255:9 | LL | return true; | ^^^^^^^^^^^ @@ -497,7 +497,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:250:9 + --> tests/ui/needless_return.rs:257:9 | LL | return false; | ^^^^^^^^^^^^ @@ -509,7 +509,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:257:13 + --> tests/ui/needless_return.rs:264:13 | LL | return 10; | ^^^^^^^^^ @@ -524,7 +524,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:260:13 + --> tests/ui/needless_return.rs:267:13 | LL | return 100; | ^^^^^^^^^^ @@ -537,7 +537,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:268:9 + --> tests/ui/needless_return.rs:275:9 | LL | return 0; | ^^^^^^^^ @@ -549,7 +549,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:275:13 + --> tests/ui/needless_return.rs:282:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -564,7 +564,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:277:13 + --> tests/ui/needless_return.rs:284:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -577,7 +577,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:284:20 + --> tests/ui/needless_return.rs:291:20 | LL | let _ = 42; | ____________________^ @@ -594,7 +594,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:291:20 + --> tests/ui/needless_return.rs:298:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -606,7 +606,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:303:9 + --> tests/ui/needless_return.rs:310:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -618,7 +618,7 @@ LL + Ok(format!("ok!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:305:9 + --> tests/ui/needless_return.rs:312:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -630,7 +630,7 @@ LL + Err(format!("err!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:311:9 + --> tests/ui/needless_return.rs:318:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -642,7 +642,7 @@ LL + if true { 1 } else { 2 } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:315:9 + --> tests/ui/needless_return.rs:322:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -654,7 +654,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else | error: unneeded `return` statement - --> tests/ui/needless_return.rs:336:5 + --> tests/ui/needless_return.rs:343:5 | LL | return { "a".to_string() } + "b" + { "c" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^