Skip to content

Commit 47c9145

Browse files
committed
Auto merge of #9497 - kraktus:needless_return2, r=llogiq
[`needless_return`] Recursively remove unneeded semicolons fix #8336, fix #8156, fix #7358, fix #9192, fix #9503 changelog: [`needless_return`] Recursively remove unneeded semicolons For now the suggestion about removing the semicolons are hidden because they would be very noisy and should be obvious if the user wants to apply the lint manually instead of using `--fix`. This could be an issue for beginner, but haven't found better way to display it.
2 parents d31db02 + b333645 commit 47c9145

File tree

4 files changed

+316
-136
lines changed

4 files changed

+316
-136
lines changed

clippy_lints/src/returns.rs

Lines changed: 75 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::diagnostics::span_lint_hir_and_then;
1+
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
22
use clippy_utils::source::{snippet_opt, snippet_with_context};
33
use clippy_utils::{fn_def_id, path_to_local_id};
44
use if_chain::if_chain;
@@ -72,6 +72,27 @@ enum RetReplacement {
7272
Unit,
7373
}
7474

75+
impl RetReplacement {
76+
fn sugg_help(self) -> &'static str {
77+
match self {
78+
Self::Empty => "remove `return`",
79+
Self::Block => "replace `return` with an empty block",
80+
Self::Unit => "replace `return` with a unit value",
81+
}
82+
}
83+
}
84+
85+
impl ToString for RetReplacement {
86+
fn to_string(&self) -> String {
87+
match *self {
88+
Self::Empty => "",
89+
Self::Block => "{}",
90+
Self::Unit => "()",
91+
}
92+
.to_string()
93+
}
94+
}
95+
7596
declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
7697

7798
impl<'tcx> LateLintPass<'tcx> for Return {
@@ -139,62 +160,67 @@ impl<'tcx> LateLintPass<'tcx> for Return {
139160
} else {
140161
RetReplacement::Empty
141162
};
142-
check_final_expr(cx, body.value, Some(body.value.span), replacement);
163+
check_final_expr(cx, body.value, vec![], replacement);
143164
},
144165
FnKind::ItemFn(..) | FnKind::Method(..) => {
145-
if let ExprKind::Block(block, _) = body.value.kind {
146-
check_block_return(cx, block);
147-
}
166+
check_block_return(cx, &body.value.kind, vec![]);
148167
},
149168
}
150169
}
151170
}
152171

153-
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
154-
if let Some(expr) = block.expr {
155-
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
156-
} else if let Some(stmt) = block.stmts.iter().last() {
157-
match stmt.kind {
158-
StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
159-
check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
160-
},
161-
_ => (),
172+
// if `expr` is a block, check if there are needless returns in it
173+
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec<Span>) {
174+
if let ExprKind::Block(block, _) = expr_kind {
175+
if let Some(block_expr) = block.expr {
176+
check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
177+
} else if let Some(stmt) = block.stmts.iter().last() {
178+
match stmt.kind {
179+
StmtKind::Expr(expr) => {
180+
check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
181+
},
182+
StmtKind::Semi(semi_expr) => {
183+
let mut semi_spans_and_this_one = semi_spans;
184+
// we only want the span containing the semicolon so we can remove it later. From `entry.rs:382`
185+
if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) {
186+
semi_spans_and_this_one.push(semicolon_span);
187+
check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty);
188+
}
189+
},
190+
_ => (),
191+
}
162192
}
163193
}
164194
}
165195

166196
fn check_final_expr<'tcx>(
167197
cx: &LateContext<'tcx>,
168198
expr: &'tcx Expr<'tcx>,
169-
span: Option<Span>,
199+
semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
200+
* needless return */
170201
replacement: RetReplacement,
171202
) {
172-
match expr.kind {
203+
let peeled_drop_expr = expr.peel_drop_temps();
204+
match &peeled_drop_expr.kind {
173205
// simple return is always "bad"
174206
ExprKind::Ret(ref inner) => {
175207
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
176208
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
177209
if !borrows {
178210
emit_return_lint(
179211
cx,
180-
inner.map_or(expr.hir_id, |inner| inner.hir_id),
181-
span.expect("`else return` is not possible"),
212+
peeled_drop_expr.span,
213+
semi_spans,
182214
inner.as_ref().map(|i| i.span),
183215
replacement,
184216
);
185217
}
186218
}
187219
},
188-
// a whole block? check it!
189-
ExprKind::Block(block, _) => {
190-
check_block_return(cx, block);
191-
},
192220
ExprKind::If(_, then, else_clause_opt) => {
193-
if let ExprKind::Block(ifblock, _) = then.kind {
194-
check_block_return(cx, ifblock);
195-
}
221+
check_block_return(cx, &then.kind, semi_spans.clone());
196222
if let Some(else_clause) = else_clause_opt {
197-
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
223+
check_block_return(cx, &else_clause.kind, semi_spans);
198224
}
199225
},
200226
// a match expr, check all arms
@@ -203,93 +229,44 @@ fn check_final_expr<'tcx>(
203229
// (except for unit type functions) so we don't match it
204230
ExprKind::Match(_, arms, MatchSource::Normal) => {
205231
for arm in arms.iter() {
206-
check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit);
232+
check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
207233
}
208234
},
209-
ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
210-
_ => (),
235+
// if it's a whole block, check it
236+
other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans),
211237
}
212238
}
213239

214240
fn emit_return_lint(
215241
cx: &LateContext<'_>,
216-
emission_place: HirId,
217242
ret_span: Span,
243+
semi_spans: Vec<Span>,
218244
inner_span: Option<Span>,
219245
replacement: RetReplacement,
220246
) {
221247
if ret_span.from_expansion() {
222248
return;
223249
}
224-
match inner_span {
225-
Some(inner_span) => {
226-
let mut applicability = Applicability::MachineApplicable;
227-
span_lint_hir_and_then(
228-
cx,
229-
NEEDLESS_RETURN,
230-
emission_place,
231-
ret_span,
232-
"unneeded `return` statement",
233-
|diag| {
234-
let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
235-
diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
236-
},
237-
);
238-
},
239-
None => match replacement {
240-
RetReplacement::Empty => {
241-
span_lint_hir_and_then(
242-
cx,
243-
NEEDLESS_RETURN,
244-
emission_place,
245-
ret_span,
246-
"unneeded `return` statement",
247-
|diag| {
248-
diag.span_suggestion(
249-
ret_span,
250-
"remove `return`",
251-
String::new(),
252-
Applicability::MachineApplicable,
253-
);
254-
},
255-
);
256-
},
257-
RetReplacement::Block => {
258-
span_lint_hir_and_then(
259-
cx,
260-
NEEDLESS_RETURN,
261-
emission_place,
262-
ret_span,
263-
"unneeded `return` statement",
264-
|diag| {
265-
diag.span_suggestion(
266-
ret_span,
267-
"replace `return` with an empty block",
268-
"{}".to_string(),
269-
Applicability::MachineApplicable,
270-
);
271-
},
272-
);
273-
},
274-
RetReplacement::Unit => {
275-
span_lint_hir_and_then(
276-
cx,
277-
NEEDLESS_RETURN,
278-
emission_place,
279-
ret_span,
280-
"unneeded `return` statement",
281-
|diag| {
282-
diag.span_suggestion(
283-
ret_span,
284-
"replace `return` with a unit value",
285-
"()".to_string(),
286-
Applicability::MachineApplicable,
287-
);
288-
},
289-
);
290-
},
250+
let mut applicability = Applicability::MachineApplicable;
251+
let return_replacement = inner_span.map_or_else(
252+
|| replacement.to_string(),
253+
|inner_span| {
254+
let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
255+
snippet.to_string()
291256
},
292-
}
257+
);
258+
let sugg_help = if inner_span.is_some() {
259+
"remove `return`"
260+
} else {
261+
replacement.sugg_help()
262+
};
263+
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
264+
diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
265+
// for each parent statement, we need to remove the semicolon
266+
for semi_stmt_span in semi_spans {
267+
diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability);
268+
}
269+
});
293270
}
294271

295272
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {

tests/ui/needless_return.fixed

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,41 @@ fn issue_9361() -> i32 {
233233
return 1 + 2;
234234
}
235235

236+
fn issue8336(x: i32) -> bool {
237+
if x > 0 {
238+
println!("something");
239+
true
240+
} else {
241+
false
242+
}
243+
}
244+
245+
fn issue8156(x: u8) -> u64 {
246+
match x {
247+
80 => {
248+
10
249+
},
250+
_ => {
251+
100
252+
},
253+
}
254+
}
255+
256+
// Ideally the compiler should throw `unused_braces` in this case
257+
fn issue9192() -> i32 {
258+
{
259+
0
260+
}
261+
}
262+
263+
fn issue9503(x: usize) -> isize {
264+
unsafe {
265+
if x > 12 {
266+
*(x as *const isize)
267+
} else {
268+
!*(x as *const isize)
269+
}
270+
}
271+
}
272+
236273
fn main() {}

tests/ui/needless_return.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,41 @@ fn issue_9361() -> i32 {
233233
return 1 + 2;
234234
}
235235

236+
fn issue8336(x: i32) -> bool {
237+
if x > 0 {
238+
println!("something");
239+
return true;
240+
} else {
241+
return false;
242+
};
243+
}
244+
245+
fn issue8156(x: u8) -> u64 {
246+
match x {
247+
80 => {
248+
return 10;
249+
},
250+
_ => {
251+
return 100;
252+
},
253+
};
254+
}
255+
256+
// Ideally the compiler should throw `unused_braces` in this case
257+
fn issue9192() -> i32 {
258+
{
259+
return 0;
260+
};
261+
}
262+
263+
fn issue9503(x: usize) -> isize {
264+
unsafe {
265+
if x > 12 {
266+
return *(x as *const isize);
267+
} else {
268+
return !*(x as *const isize);
269+
};
270+
};
271+
}
272+
236273
fn main() {}

0 commit comments

Comments
 (0)