Skip to content

Commit

Permalink
new_line loop_without_break_or_return
Browse files Browse the repository at this point in the history
Signed-off-by: mojave2 <[email protected]>
  • Loading branch information
kiscad committed Jun 26, 2023
1 parent d5a1eff commit 4d8af6f
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4652,6 +4652,7 @@ Released 2018-09-13
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
[`loop_without_break_or_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#loop_without_break_or_return
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO,
crate::literal_representation::UNREADABLE_LITERAL_INFO,
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
crate::loop_without_break_or_return::LOOP_WITHOUT_BREAK_OR_RETURN_INFO,
crate::loops::EMPTY_LOOP_INFO,
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ mod let_with_type_underscore;
mod lifetimes;
mod lines_filter_map_ok;
mod literal_representation;
mod loop_without_break_or_return;
mod loops;
mod macro_use;
mod main_recursion;
Expand Down Expand Up @@ -968,6 +969,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(unsafe_block_in_proc_macro::UnsafeBlockInProcMacro::new()));
store.register_early_pass(|| Box::new(implicit_abi::ImplicitAbi));
store.register_early_pass(|| Box::new(non_reentrant_functions::NonReentrantFunctions));
store.register_early_pass(|| Box::new(loop_without_break_or_return::LoopWithoutBreakOrReturn));
// add lints here, do not remove this comment, it's used in `new_lint`
}

Expand Down
82 changes: 82 additions & 0 deletions clippy_lints/src/loop_without_break_or_return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{Block, Expr, ExprKind, Label, StmtKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
/// ### What it does
/// Checks for loop-without-exit-mechanism.
///
/// ### Why is this bad?
/// This makes code bug-prone.
///
/// ### Example
/// ```rust
/// loop {
/// println!("so something");
/// }
/// ```
/// Use instead:
/// ```rust
/// loop {
/// println!("do something");
/// if flag {
/// break;
/// }
/// }
/// ```
#[clippy::version = "1.70.0"]
pub LOOP_WITHOUT_BREAK_OR_RETURN,
nursery,
"loop block without `break` or `return` statement"
}
declare_lint_pass!(LoopWithoutBreakOrReturn => [LOOP_WITHOUT_BREAK_OR_RETURN]);

impl EarlyLintPass for LoopWithoutBreakOrReturn {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if expr.span.from_expansion() {
return;
}

let msg: &str = "consider adding `break` or `return` statement in the loop block";

if let ExprKind::Loop(block, label, _) = &expr.kind {
if !check_block(block, label, true) {
span_lint(cx, LOOP_WITHOUT_BREAK_OR_RETURN, expr.span, msg);
}
}
}
}

fn check_block(block: &Block, label: &Option<Label>, outest: bool) -> bool {
block.stmts.iter().any(|stmt| match &stmt.kind {
StmtKind::Semi(expr) | StmtKind::Expr(expr) => !expr.span.from_expansion() && check_expr(expr, label, outest),
_ => false,
})
}

fn check_expr(expr: &Expr, label: &Option<Label>, outest: bool) -> bool {
match &expr.kind {
ExprKind::Ret(..) => true,
ExprKind::Break(lbl, _) => {
if outest {
true
} else {
label.is_some() && label == lbl
}
},
ExprKind::If(_, blk, else_expr) => {
let mut do_exit = check_block(blk, label, outest);
if let Some(expr) = else_expr {
do_exit = do_exit || check_expr(expr, label, outest);
}
do_exit
},
ExprKind::Loop(blk, ..) | ExprKind::ForLoop(_, _, blk, _) | ExprKind::While(_, blk, _) => {
check_block(blk, label, false)
},
ExprKind::Block(blk, _) | ExprKind::Async(_, blk) => check_block(blk, label, outest),
ExprKind::Match(_, arms) => arms.iter().any(|arm| check_expr(&arm.body, label, outest)),
_ => false,
}
}
102 changes: 102 additions & 0 deletions tests/ui/loop_without_break_or_return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#![allow(unused, clippy::never_loop)]
#![warn(clippy::loop_without_break_or_return)]

fn test_01() {
loop {
println!("Hello, Rust!");
}

loop {
break;
}

'outer: loop {
break 'outer;
}

'outer: loop {
break;
}
}

fn test_02() {
loop {
if 2 < 3 {
break;
}
}
}

fn test_03() {
'outer1: loop {
for x in 0..5 {
if x == 3 {
break 'outer1;
}
}
}

'outer2: loop {
for x in 0..5 {
if x == 3 {
break;
}
}
}

'outer3: loop {
for x in 0..5 {
if x == 3 {
println!("Hello, Rust!");
} else {
break 'outer3;
}
}
}
}

fn test_04() {
'outer1: loop {
loop {
println!("Hello, Rust!");
}
break;
}

'outer2: loop {
loop {
break;
}
}

'outer3: loop {
loop {
break 'outer3;
}
}

'outer4: loop {
'inner: loop {
loop {
break 'inner;
}
}
}

'outer5: loop {
loop {
'inner: loop {
loop {
loop {
break 'inner;
}
break 'outer5;
}
}
}
}
}

fn main() {
// test code goes here
}
66 changes: 66 additions & 0 deletions tests/ui/loop_without_break_or_return.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:5:5
|
LL | / loop {
LL | | println!("Hello, Rust!");
LL | | }
| |_____^
|
= note: `-D clippy::loop-without-break-or-return` implied by `-D warnings`

error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:39:5
|
LL | / 'outer2: loop {
LL | | for x in 0..5 {
LL | | if x == 3 {
LL | | break;
LL | | }
LL | | }
LL | | }
| |_____^

error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:60:9
|
LL | / loop {
LL | | println!("Hello, Rust!");
LL | | }
| |_________^

error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:66:5
|
LL | / 'outer2: loop {
LL | | loop {
LL | | break;
LL | | }
LL | | }
| |_____^

error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:78:5
|
LL | / 'outer4: loop {
LL | | 'inner: loop {
LL | | loop {
LL | | break 'inner;
LL | | }
LL | | }
LL | | }
| |_____^

error: consider adding `break` or `return` statement in the loop block
--> $DIR/loop_without_break_or_return.rs:87:9
|
LL | / loop {
LL | | 'inner: loop {
LL | | loop {
LL | | loop {
... |
LL | | }
LL | | }
| |_________^

error: aborting due to 6 previous errors

0 comments on commit 4d8af6f

Please sign in to comment.