Skip to content

Commit 3a413b7

Browse files
authored
Merge pull request rust-lang#5 from mojave2/lint_loop_exit
new_lint loop_without_break_or_return
2 parents d5a1eff + 4d8af6f commit 3a413b7

File tree

6 files changed

+254
-0
lines changed

6 files changed

+254
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4652,6 +4652,7 @@ Released 2018-09-13
46524652
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
46534653
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
46544654
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
4655+
[`loop_without_break_or_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#loop_without_break_or_return
46554656
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
46564657
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
46574658
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
244244
crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO,
245245
crate::literal_representation::UNREADABLE_LITERAL_INFO,
246246
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
247+
crate::loop_without_break_or_return::LOOP_WITHOUT_BREAK_OR_RETURN_INFO,
247248
crate::loops::EMPTY_LOOP_INFO,
248249
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
249250
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ mod let_with_type_underscore;
173173
mod lifetimes;
174174
mod lines_filter_map_ok;
175175
mod literal_representation;
176+
mod loop_without_break_or_return;
176177
mod loops;
177178
mod macro_use;
178179
mod main_recursion;
@@ -968,6 +969,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
968969
store.register_late_pass(|_| Box::new(unsafe_block_in_proc_macro::UnsafeBlockInProcMacro::new()));
969970
store.register_early_pass(|| Box::new(implicit_abi::ImplicitAbi));
970971
store.register_early_pass(|| Box::new(non_reentrant_functions::NonReentrantFunctions));
972+
store.register_early_pass(|| Box::new(loop_without_break_or_return::LoopWithoutBreakOrReturn));
971973
// add lints here, do not remove this comment, it's used in `new_lint`
972974
}
973975

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_ast::ast::{Block, Expr, ExprKind, Label, StmtKind};
3+
use rustc_lint::{EarlyContext, EarlyLintPass};
4+
use rustc_session::{declare_lint_pass, declare_tool_lint};
5+
6+
declare_clippy_lint! {
7+
/// ### What it does
8+
/// Checks for loop-without-exit-mechanism.
9+
///
10+
/// ### Why is this bad?
11+
/// This makes code bug-prone.
12+
///
13+
/// ### Example
14+
/// ```rust
15+
/// loop {
16+
/// println!("so something");
17+
/// }
18+
/// ```
19+
/// Use instead:
20+
/// ```rust
21+
/// loop {
22+
/// println!("do something");
23+
/// if flag {
24+
/// break;
25+
/// }
26+
/// }
27+
/// ```
28+
#[clippy::version = "1.70.0"]
29+
pub LOOP_WITHOUT_BREAK_OR_RETURN,
30+
nursery,
31+
"loop block without `break` or `return` statement"
32+
}
33+
declare_lint_pass!(LoopWithoutBreakOrReturn => [LOOP_WITHOUT_BREAK_OR_RETURN]);
34+
35+
impl EarlyLintPass for LoopWithoutBreakOrReturn {
36+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
37+
if expr.span.from_expansion() {
38+
return;
39+
}
40+
41+
let msg: &str = "consider adding `break` or `return` statement in the loop block";
42+
43+
if let ExprKind::Loop(block, label, _) = &expr.kind {
44+
if !check_block(block, label, true) {
45+
span_lint(cx, LOOP_WITHOUT_BREAK_OR_RETURN, expr.span, msg);
46+
}
47+
}
48+
}
49+
}
50+
51+
fn check_block(block: &Block, label: &Option<Label>, outest: bool) -> bool {
52+
block.stmts.iter().any(|stmt| match &stmt.kind {
53+
StmtKind::Semi(expr) | StmtKind::Expr(expr) => !expr.span.from_expansion() && check_expr(expr, label, outest),
54+
_ => false,
55+
})
56+
}
57+
58+
fn check_expr(expr: &Expr, label: &Option<Label>, outest: bool) -> bool {
59+
match &expr.kind {
60+
ExprKind::Ret(..) => true,
61+
ExprKind::Break(lbl, _) => {
62+
if outest {
63+
true
64+
} else {
65+
label.is_some() && label == lbl
66+
}
67+
},
68+
ExprKind::If(_, blk, else_expr) => {
69+
let mut do_exit = check_block(blk, label, outest);
70+
if let Some(expr) = else_expr {
71+
do_exit = do_exit || check_expr(expr, label, outest);
72+
}
73+
do_exit
74+
},
75+
ExprKind::Loop(blk, ..) | ExprKind::ForLoop(_, _, blk, _) | ExprKind::While(_, blk, _) => {
76+
check_block(blk, label, false)
77+
},
78+
ExprKind::Block(blk, _) | ExprKind::Async(_, blk) => check_block(blk, label, outest),
79+
ExprKind::Match(_, arms) => arms.iter().any(|arm| check_expr(&arm.body, label, outest)),
80+
_ => false,
81+
}
82+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#![allow(unused, clippy::never_loop)]
2+
#![warn(clippy::loop_without_break_or_return)]
3+
4+
fn test_01() {
5+
loop {
6+
println!("Hello, Rust!");
7+
}
8+
9+
loop {
10+
break;
11+
}
12+
13+
'outer: loop {
14+
break 'outer;
15+
}
16+
17+
'outer: loop {
18+
break;
19+
}
20+
}
21+
22+
fn test_02() {
23+
loop {
24+
if 2 < 3 {
25+
break;
26+
}
27+
}
28+
}
29+
30+
fn test_03() {
31+
'outer1: loop {
32+
for x in 0..5 {
33+
if x == 3 {
34+
break 'outer1;
35+
}
36+
}
37+
}
38+
39+
'outer2: loop {
40+
for x in 0..5 {
41+
if x == 3 {
42+
break;
43+
}
44+
}
45+
}
46+
47+
'outer3: loop {
48+
for x in 0..5 {
49+
if x == 3 {
50+
println!("Hello, Rust!");
51+
} else {
52+
break 'outer3;
53+
}
54+
}
55+
}
56+
}
57+
58+
fn test_04() {
59+
'outer1: loop {
60+
loop {
61+
println!("Hello, Rust!");
62+
}
63+
break;
64+
}
65+
66+
'outer2: loop {
67+
loop {
68+
break;
69+
}
70+
}
71+
72+
'outer3: loop {
73+
loop {
74+
break 'outer3;
75+
}
76+
}
77+
78+
'outer4: loop {
79+
'inner: loop {
80+
loop {
81+
break 'inner;
82+
}
83+
}
84+
}
85+
86+
'outer5: loop {
87+
loop {
88+
'inner: loop {
89+
loop {
90+
loop {
91+
break 'inner;
92+
}
93+
break 'outer5;
94+
}
95+
}
96+
}
97+
}
98+
}
99+
100+
fn main() {
101+
// test code goes here
102+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: consider adding `break` or `return` statement in the loop block
2+
--> $DIR/loop_without_break_or_return.rs:5:5
3+
|
4+
LL | / loop {
5+
LL | | println!("Hello, Rust!");
6+
LL | | }
7+
| |_____^
8+
|
9+
= note: `-D clippy::loop-without-break-or-return` implied by `-D warnings`
10+
11+
error: consider adding `break` or `return` statement in the loop block
12+
--> $DIR/loop_without_break_or_return.rs:39:5
13+
|
14+
LL | / 'outer2: loop {
15+
LL | | for x in 0..5 {
16+
LL | | if x == 3 {
17+
LL | | break;
18+
LL | | }
19+
LL | | }
20+
LL | | }
21+
| |_____^
22+
23+
error: consider adding `break` or `return` statement in the loop block
24+
--> $DIR/loop_without_break_or_return.rs:60:9
25+
|
26+
LL | / loop {
27+
LL | | println!("Hello, Rust!");
28+
LL | | }
29+
| |_________^
30+
31+
error: consider adding `break` or `return` statement in the loop block
32+
--> $DIR/loop_without_break_or_return.rs:66:5
33+
|
34+
LL | / 'outer2: loop {
35+
LL | | loop {
36+
LL | | break;
37+
LL | | }
38+
LL | | }
39+
| |_____^
40+
41+
error: consider adding `break` or `return` statement in the loop block
42+
--> $DIR/loop_without_break_or_return.rs:78:5
43+
|
44+
LL | / 'outer4: loop {
45+
LL | | 'inner: loop {
46+
LL | | loop {
47+
LL | | break 'inner;
48+
LL | | }
49+
LL | | }
50+
LL | | }
51+
| |_____^
52+
53+
error: consider adding `break` or `return` statement in the loop block
54+
--> $DIR/loop_without_break_or_return.rs:87:9
55+
|
56+
LL | / loop {
57+
LL | | 'inner: loop {
58+
LL | | loop {
59+
LL | | loop {
60+
... |
61+
LL | | }
62+
LL | | }
63+
| |_________^
64+
65+
error: aborting due to 6 previous errors
66+

0 commit comments

Comments
 (0)