Skip to content

Commit b9b4537

Browse files
committed
Auto merge of rust-lang#10945 - Centri3:no_effect, r=llogiq
[`no_effect`]: Suggest adding `return` if applicable Closes rust-lang#10941 Unfortunately doesn't catch anything complex as `no_effect` already wouldn't, but I'm fine with that (it catches `ControlFlow` at least :D) changelog: [`no_effect`]: Suggest adding `return` if statement has same type as function's return type and is the last statement in a block
2 parents 1b7fb40 + d255e7a commit b9b4537

File tree

3 files changed

+195
-3
lines changed

3 files changed

+195
-3
lines changed

clippy_lints/src/no_effect.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
2-
use clippy_utils::is_lint_allowed;
32
use clippy_utils::peel_blocks;
43
use clippy_utils::source::snippet_opt;
54
use clippy_utils::ty::has_drop;
5+
use clippy_utils::{get_parent_node, is_lint_allowed};
66
use rustc_errors::Applicability;
77
use rustc_hir::def::{DefKind, Res};
8-
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
8+
use rustc_hir::{
9+
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind,
10+
UnsafeSource,
11+
};
12+
use rustc_hir_analysis::hir_ty_to_ty;
13+
use rustc_infer::infer::TyCtxtInferExt as _;
914
use rustc_lint::{LateContext, LateLintPass, LintContext};
1015
use rustc_middle::lint::in_external_macro;
1116
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -86,7 +91,43 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
8691
fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
8792
if let StmtKind::Semi(expr) = stmt.kind {
8893
if has_no_effect(cx, expr) {
89-
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
94+
span_lint_hir_and_then(
95+
cx,
96+
NO_EFFECT,
97+
expr.hir_id,
98+
stmt.span,
99+
"statement with no effect",
100+
|diag| {
101+
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
102+
if let Node::Item(item) = parent.1
103+
&& let ItemKind::Fn(sig, ..) = item.kind
104+
&& let FnRetTy::Return(ret_ty) = sig.decl.output
105+
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
106+
&& let [.., final_stmt] = block.stmts
107+
&& final_stmt.hir_id == stmt.hir_id
108+
{
109+
let expr_ty = cx.typeck_results().expr_ty(expr);
110+
let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty);
111+
112+
// Remove `impl Future<Output = T>` to get `T`
113+
if cx.tcx.ty_is_opaque_future(ret_ty) &&
114+
let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
115+
{
116+
ret_ty = true_ret_ty;
117+
}
118+
119+
if ret_ty == expr_ty {
120+
diag.span_suggestion(
121+
stmt.span.shrink_to_lo(),
122+
"did you mean to return it?",
123+
"return ",
124+
Applicability::MaybeIncorrect,
125+
);
126+
}
127+
}
128+
}
129+
},
130+
);
90131
return true;
91132
}
92133
} else if let StmtKind::Local(local) = stmt.kind {

tests/ui/no_effect_return.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#![allow(clippy::unused_unit, dead_code, unused)]
2+
#![no_main]
3+
4+
use std::ops::ControlFlow;
5+
6+
fn a() -> u32 {
7+
{
8+
0u32;
9+
}
10+
0
11+
}
12+
13+
async fn b() -> u32 {
14+
{
15+
0u32;
16+
}
17+
0
18+
}
19+
20+
type C = i32;
21+
async fn c() -> C {
22+
{
23+
0i32 as C;
24+
}
25+
0
26+
}
27+
28+
fn d() -> u128 {
29+
{
30+
// not last stmt
31+
0u128;
32+
println!("lol");
33+
}
34+
0
35+
}
36+
37+
fn e() -> u32 {
38+
{
39+
// mismatched types
40+
0u16;
41+
}
42+
0
43+
}
44+
45+
fn f() -> [u16; 1] {
46+
{
47+
[1u16];
48+
}
49+
[1]
50+
}
51+
52+
fn g() -> ControlFlow<()> {
53+
{
54+
ControlFlow::Break::<()>(());
55+
}
56+
ControlFlow::Continue(())
57+
}
58+
59+
fn h() -> Vec<u16> {
60+
{
61+
// function call, so this won't trigger `no_effect`. not an issue with this change, but the
62+
// lint itself (but also not really.)
63+
vec![0u16];
64+
}
65+
vec![]
66+
}
67+
68+
fn i() -> () {
69+
{
70+
();
71+
}
72+
()
73+
}
74+
75+
fn j() {
76+
{
77+
// does not suggest on function without explicit return type
78+
();
79+
}
80+
()
81+
}

tests/ui/no_effect_return.stderr

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
error: statement with no effect
2+
--> $DIR/no_effect_return.rs:8:9
3+
|
4+
LL | 0u32;
5+
| -^^^^
6+
| |
7+
| help: did you mean to return it?: `return`
8+
|
9+
= note: `-D clippy::no-effect` implied by `-D warnings`
10+
11+
error: statement with no effect
12+
--> $DIR/no_effect_return.rs:15:9
13+
|
14+
LL | 0u32;
15+
| -^^^^
16+
| |
17+
| help: did you mean to return it?: `return`
18+
19+
error: statement with no effect
20+
--> $DIR/no_effect_return.rs:23:9
21+
|
22+
LL | 0i32 as C;
23+
| -^^^^^^^^^
24+
| |
25+
| help: did you mean to return it?: `return`
26+
27+
error: statement with no effect
28+
--> $DIR/no_effect_return.rs:31:9
29+
|
30+
LL | 0u128;
31+
| ^^^^^^
32+
33+
error: statement with no effect
34+
--> $DIR/no_effect_return.rs:40:9
35+
|
36+
LL | 0u16;
37+
| ^^^^^
38+
39+
error: statement with no effect
40+
--> $DIR/no_effect_return.rs:47:9
41+
|
42+
LL | [1u16];
43+
| -^^^^^^
44+
| |
45+
| help: did you mean to return it?: `return`
46+
47+
error: statement with no effect
48+
--> $DIR/no_effect_return.rs:54:9
49+
|
50+
LL | ControlFlow::Break::<()>(());
51+
| -^^^^^^^^^^^^^^^^^^^^^^^^^^^^
52+
| |
53+
| help: did you mean to return it?: `return`
54+
55+
error: statement with no effect
56+
--> $DIR/no_effect_return.rs:70:9
57+
|
58+
LL | ();
59+
| -^^
60+
| |
61+
| help: did you mean to return it?: `return`
62+
63+
error: statement with no effect
64+
--> $DIR/no_effect_return.rs:78:9
65+
|
66+
LL | ();
67+
| ^^^
68+
69+
error: aborting due to 9 previous errors
70+

0 commit comments

Comments
 (0)