Skip to content

Commit 07a7603

Browse files
committed
Auto merge of #10187 - dswij:issue-10182-semicolon, r=Jarcho
[needless_return]: Remove all semicolons on suggestion Closes #10182 Multiple semicolons currently breaks autofix for `needless_return` suggestions. Any semicolons left after removing return means that the return type will always be `()`, and thus fail to compile. This PR allows `needless_return` to remove multiple semicolons. The change won't cover the case where there is multiple line yet. i.e. ```rust fn needless_return() -> bool { return true; ;; } ``` --- changelog: Sugg: [`needless_return`]: Now removes all semicolons on the same line [#10187](#10187) <!-- changelog_checked -->
2 parents aceb443 + d73adea commit 07a7603

File tree

5 files changed

+101
-59
lines changed

5 files changed

+101
-59
lines changed

clippy_lints/src/returns.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
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::visitors::{for_each_expr, Descend};
4-
use clippy_utils::{fn_def_id, path_to_local_id};
4+
use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi};
55
use core::ops::ControlFlow;
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
@@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
151151
kind: FnKind<'tcx>,
152152
_: &'tcx FnDecl<'tcx>,
153153
body: &'tcx Body<'tcx>,
154-
_: Span,
154+
sp: Span,
155155
_: HirId,
156156
) {
157157
match kind {
@@ -166,14 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Return {
166166
check_final_expr(cx, body.value, vec![], replacement);
167167
},
168168
FnKind::ItemFn(..) | FnKind::Method(..) => {
169-
check_block_return(cx, &body.value.kind, vec![]);
169+
check_block_return(cx, &body.value.kind, sp, vec![]);
170170
},
171171
}
172172
}
173173
}
174174

175175
// if `expr` is a block, check if there are needless returns in it
176-
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec<Span>) {
176+
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
177177
if let ExprKind::Block(block, _) = expr_kind {
178178
if let Some(block_expr) = block.expr {
179179
check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
@@ -183,12 +183,14 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>,
183183
check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
184184
},
185185
StmtKind::Semi(semi_expr) => {
186-
let mut semi_spans_and_this_one = semi_spans;
187-
// we only want the span containing the semicolon so we can remove it later. From `entry.rs:382`
188-
if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) {
189-
semi_spans_and_this_one.push(semicolon_span);
190-
check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty);
186+
// Remove ending semicolons and any whitespace ' ' in between.
187+
// Without `return`, the suggestion might not compile if the semicolon is retained
188+
if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) {
189+
let semi_span_to_remove =
190+
span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
191+
semi_spans.push(semi_span_to_remove);
191192
}
193+
check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty);
192194
},
193195
_ => (),
194196
}
@@ -231,9 +233,9 @@ fn check_final_expr<'tcx>(
231233
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
232234
},
233235
ExprKind::If(_, then, else_clause_opt) => {
234-
check_block_return(cx, &then.kind, semi_spans.clone());
236+
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
235237
if let Some(else_clause) = else_clause_opt {
236-
check_block_return(cx, &else_clause.kind, semi_spans);
238+
check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans);
237239
}
238240
},
239241
// a match expr, check all arms
@@ -246,7 +248,7 @@ fn check_final_expr<'tcx>(
246248
}
247249
},
248250
// if it's a whole block, check it
249-
other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans),
251+
other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans),
250252
}
251253
}
252254

clippy_utils/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,10 @@ pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
24912491
comments_buf.join("\n")
24922492
}
24932493

2494+
pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2495+
sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2496+
}
2497+
24942498
macro_rules! op_utils {
24952499
($($name:ident $assign:ident)*) => {
24962500
/// Binary operation traits like `LangItem::Add`

tests/ui/needless_return.fixed

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ fn test_no_semicolon() -> bool {
3131
true
3232
}
3333

34+
#[rustfmt::skip]
35+
fn test_multiple_semicolon() -> bool {
36+
true
37+
}
38+
39+
#[rustfmt::skip]
40+
fn test_multiple_semicolon_with_spaces() -> bool {
41+
true
42+
}
43+
3444
fn test_if_block() -> bool {
3545
if true {
3646
true

tests/ui/needless_return.rs

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ fn test_no_semicolon() -> bool {
3131
return true;
3232
}
3333

34+
#[rustfmt::skip]
35+
fn test_multiple_semicolon() -> bool {
36+
return true;;;
37+
}
38+
39+
#[rustfmt::skip]
40+
fn test_multiple_semicolon_with_spaces() -> bool {
41+
return true;; ; ;
42+
}
43+
3444
fn test_if_block() -> bool {
3545
if true {
3646
return true;

0 commit comments

Comments
 (0)