Skip to content

Commit 4a45997

Browse files
committed
[needless_continue]: lint if the last stmt in for/while/loop is continue, recursively
fixes: #4077
1 parent c556695 commit 4a45997

7 files changed

+179
-26
lines changed

Diff for: clippy_lints/src/methods/unnecessary_to_owned.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
379379
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
380380
match node {
381381
Node::Stmt(_) => return true,
382-
Node::Block(..) => continue,
382+
Node::Block(..) => {}
383383
Node::Item(item) => {
384384
if let ItemKind::Fn(_, _, body_id) = &item.kind
385385
&& let output_ty = return_ty(cx, item.owner_id)

Diff for: clippy_lints/src/needless_continue.rs

+111-16
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
//! This lint is **warn** by default.
3636
use clippy_utils::diagnostics::span_lint_and_help;
3737
use clippy_utils::source::{indent_of, snippet, snippet_block};
38-
use rustc_ast::ast;
38+
use rustc_ast::{ast, Block, Label};
3939
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
4040
use rustc_session::{declare_lint_pass, declare_tool_lint};
4141
use rustc_span::Span;
@@ -46,6 +46,7 @@ declare_clippy_lint! {
4646
/// that contain a `continue` statement in either their main blocks or their
4747
/// `else`-blocks, when omitting the `else`-block possibly with some
4848
/// rearrangement of code can make the code easier to understand.
49+
/// The lint also checks if the last statement in the loop is a `continue`
4950
///
5051
/// ### Why is this bad?
5152
/// Having explicit `else` blocks for `if` statements
@@ -110,6 +111,45 @@ declare_clippy_lint! {
110111
/// # break;
111112
/// }
112113
/// ```
114+
///
115+
/// ```rust
116+
/// fn foo() -> std::io::ErrorKind { unimplemented!() }
117+
/// loop {
118+
/// match foo() {
119+
/// std::io::ErrorKind::NotFound => {
120+
/// eprintln!("not found");
121+
/// continue
122+
/// }
123+
/// std::io::ErrorKind::TimedOut => {
124+
/// eprintln!("timeout");
125+
/// continue
126+
/// }
127+
/// _ => {
128+
/// eprintln!("other error");
129+
/// continue
130+
/// }
131+
/// }
132+
/// }
133+
/// ```
134+
/// Could be rewritten as
135+
///
136+
///
137+
/// ```rust
138+
/// fn foo() -> std::io::ErrorKind { unimplemented!() }
139+
/// loop {
140+
/// match foo() {
141+
/// std::io::ErrorKind::NotFound => {
142+
/// eprintln!("not found");
143+
/// }
144+
/// std::io::ErrorKind::TimedOut => {
145+
/// eprintln!("timeout");
146+
/// }
147+
/// _ => {
148+
/// eprintln!("other error");
149+
/// }
150+
/// }
151+
/// }
152+
/// ```
113153
#[clippy::version = "pre 1.29.0"]
114154
pub NEEDLESS_CONTINUE,
115155
pedantic,
@@ -361,24 +401,79 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
361401
)
362402
}
363403

364-
fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
365-
if_chain! {
366-
if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind;
367-
if let Some(last_stmt) = loop_block.stmts.last();
368-
if let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind;
369-
if let ast::ExprKind::Continue(_) = inner_expr.kind;
370-
then {
371-
span_lint_and_help(
372-
cx,
373-
NEEDLESS_CONTINUE,
374-
last_stmt.span,
375-
MSG_REDUNDANT_CONTINUE_EXPRESSION,
376-
None,
377-
DROP_CONTINUE_EXPRESSION_MSG,
378-
);
404+
fn check_last_stmt_in_if<F>(b: &ast::Expr, func: &F)
405+
where
406+
F: Fn(Option<&ast::Label>, Span),
407+
{
408+
match &b.kind {
409+
ast::ExprKind::If(_, then_block, else_block) => {
410+
check_last_stmt(then_block, func);
411+
if let Some(else_block) = else_block {
412+
check_last_stmt_in_if(else_block, func);
413+
}
414+
},
415+
ast::ExprKind::Continue(..) => {
416+
unreachable!()
417+
},
418+
ast::ExprKind::Block(b, _) => {
419+
check_last_stmt(b, func);
420+
},
421+
_ => {},
422+
}
423+
}
424+
425+
fn check_last_stmt<F>(b: &Block, func: &F)
426+
where
427+
F: Fn(Option<&ast::Label>, Span),
428+
{
429+
if let Some(last_stmt) = b.stmts.last() &&
430+
let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind {
431+
match &inner_expr.kind {
432+
ast::ExprKind::Continue(continue_label) => {
433+
func(continue_label.as_ref(), last_stmt.span);
434+
},
435+
ast::ExprKind::If(_, _, _) => {
436+
check_last_stmt_in_if(inner_expr, func);
437+
}
438+
ast::ExprKind::Match(_, arms) => {
439+
for arm in arms {
440+
match &arm.body.kind {
441+
ast::ExprKind::Continue(continue_label) => {
442+
func(continue_label.as_ref(), arm.body.span);
443+
}
444+
ast::ExprKind::Block(b, _) => {
445+
check_last_stmt(b, func);
446+
447+
}
448+
_ => {}
449+
}
450+
451+
}
452+
}
453+
ast::ExprKind::Block(b, _) => {
454+
check_last_stmt(b, func);
455+
}
456+
_ => {},
379457
}
380458
}
459+
}
460+
461+
fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
381462
with_loop_block(expr, |loop_block, label| {
463+
let p = |continue_label: Option<&Label>, span: Span| {
464+
if compare_labels(label, continue_label) {
465+
span_lint_and_help(
466+
cx,
467+
NEEDLESS_CONTINUE,
468+
span,
469+
MSG_REDUNDANT_CONTINUE_EXPRESSION,
470+
None,
471+
DROP_CONTINUE_EXPRESSION_MSG,
472+
);
473+
}
474+
};
475+
check_last_stmt(loop_block, &p);
476+
382477
for (i, stmt) in loop_block.stmts.iter().enumerate() {
383478
with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
384479
let data = &LintData {

Diff for: clippy_lints/src/redundant_else.rs

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse {
6969
ExprKind::If(_, next_then, Some(next_els)) => {
7070
then = next_then;
7171
els = next_els;
72-
continue;
7372
},
7473
// else if without else
7574
ExprKind::If(..) => return,

Diff for: clippy_lints/src/transmute/transmute_undefined_repr.rs

-4
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,16 @@ pub(super) fn check<'tcx>(
3030
| (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
3131
from_ty = from_sub_ty;
3232
to_ty = to_sub_ty;
33-
continue;
3433
},
3534
(ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
3635
from_ty = from_sub_ty;
3736
to_ty = to_sub_ty;
38-
continue;
3937
},
4038
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
4139
if reduced_tys.from_fat_ptr =>
4240
{
4341
from_ty = from_sub_ty;
4442
to_ty = to_sub_ty;
45-
continue;
4643
},
4744

4845
// ptr <-> ptr
@@ -52,7 +49,6 @@ pub(super) fn check<'tcx>(
5249
{
5350
from_ty = from_sub_ty;
5451
to_ty = to_sub_ty;
55-
continue;
5652
},
5753

5854
// fat ptr <-> (*size, *size)

Diff for: tests/missing-test-files.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
6060
missing_files.push(path.to_str().unwrap().to_string());
6161
}
6262
},
63-
_ => continue,
63+
_ => {},
6464
};
6565
}
6666
}

Diff for: tests/ui/needless_continue.rs

+39
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ fn simple_loop4() {
8787
}
8888
}
8989

90+
fn simple_loop5() {
91+
loop {
92+
println!("bleh");
93+
{ continue }
94+
//~^ ERROR: this `continue` expression is redundant
95+
}
96+
}
97+
9098
mod issue_2329 {
9199
fn condition() -> bool {
92100
unimplemented!()
@@ -151,3 +159,34 @@ mod issue_2329 {
151159
}
152160
}
153161
}
162+
163+
mod issue_4077 {
164+
fn main() {
165+
'outer: loop {
166+
'inner: loop {
167+
do_something();
168+
if some_expr() {
169+
println!("bar-7");
170+
continue 'outer;
171+
} else if !some_expr() {
172+
println!("bar-8");
173+
continue 'inner;
174+
} else {
175+
println!("bar-9");
176+
continue 'inner;
177+
}
178+
}
179+
}
180+
}
181+
182+
// The contents of these functions are irrelevant, the purpose of this file is
183+
// shown in main.
184+
185+
fn do_something() {
186+
std::process::exit(0);
187+
}
188+
189+
fn some_expr() -> bool {
190+
true
191+
}
192+
}

Diff for: tests/ui/needless_continue.stderr

+27-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,16 @@ LL | continue
9191
|
9292
= help: consider dropping the `continue` expression
9393

94+
error: this `continue` expression is redundant
95+
--> $DIR/needless_continue.rs:93:11
96+
|
97+
LL | { continue }
98+
| ^^^^^^^^
99+
|
100+
= help: consider dropping the `continue` expression
101+
94102
error: this `else` block is redundant
95-
--> $DIR/needless_continue.rs:136:24
103+
--> $DIR/needless_continue.rs:144:24
96104
|
97105
LL | } else {
98106
| ________________________^
@@ -117,7 +125,7 @@ LL | | }
117125
}
118126

119127
error: there is no need for an explicit `else` block for this `if` expression
120-
--> $DIR/needless_continue.rs:143:17
128+
--> $DIR/needless_continue.rs:151:17
121129
|
122130
LL | / if condition() {
123131
LL | |
@@ -136,5 +144,21 @@ LL | | }
136144
println!("bar-5");
137145
}
138146

139-
error: aborting due to 8 previous errors
147+
error: this `continue` expression is redundant
148+
--> $DIR/needless_continue.rs:173:21
149+
|
150+
LL | continue 'inner;
151+
| ^^^^^^^^^^^^^^^^
152+
|
153+
= help: consider dropping the `continue` expression
154+
155+
error: this `continue` expression is redundant
156+
--> $DIR/needless_continue.rs:176:21
157+
|
158+
LL | continue 'inner;
159+
| ^^^^^^^^^^^^^^^^
160+
|
161+
= help: consider dropping the `continue` expression
162+
163+
error: aborting due to 11 previous errors
140164

0 commit comments

Comments
 (0)