forked from MystenLabs/sui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Linter] 'loop' without exit (MystenLabs#16875)
# Description - Gives a suspicious lint for loop without break or return. - Does not currently consider if that break or return is reachable ## Test plan - New tests ## Release notes - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: Move lint will now warn against `loop`s without `break` or `return` - [ ] Rust SDK: --------- Co-authored-by: jamedzung <[email protected]> Co-authored-by: Todd Nowacki <[email protected]>
- Loading branch information
1 parent
02fbd47
commit ba035ce
Showing
9 changed files
with
269 additions
and
3 deletions.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
external-crates/move/crates/move-compiler/src/linters/loop_without_exit.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
//! Detects empty loop expressions, including `while(true) {}` and `loop {}` without exit mechanisms, highlighting potential infinite loops. | ||
//! Aims to identify and warn against loops that may lead to hangs or excessive resource consumption due to lack of content. | ||
//! Encourages adding meaningful logic within loops or ensuring proper exit conditions to improve code reliability and maintainability. | ||
use super::StyleCodes; | ||
use crate::{ | ||
diag, | ||
diagnostics::WarningFilters, | ||
shared::CompilationEnv, | ||
typing::{ | ||
ast::{self as T, UnannotatedExp_}, | ||
visitor::{has_special_exp, TypingVisitorConstructor, TypingVisitorContext}, | ||
}, | ||
}; | ||
|
||
pub struct LoopWithoutExit; | ||
|
||
pub struct Context<'a> { | ||
env: &'a mut CompilationEnv, | ||
} | ||
|
||
impl TypingVisitorConstructor for LoopWithoutExit { | ||
type Context<'a> = Context<'a>; | ||
|
||
fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { | ||
Context { env } | ||
} | ||
} | ||
|
||
impl TypingVisitorContext for Context<'_> { | ||
fn add_warning_filter_scope(&mut self, filter: WarningFilters) { | ||
self.env.add_warning_filter_scope(filter) | ||
} | ||
fn pop_warning_filter_scope(&mut self) { | ||
self.env.pop_warning_filter_scope() | ||
} | ||
|
||
fn visit_exp_custom(&mut self, exp: &T::Exp) -> bool { | ||
// we do not care about `while` since there is another lint that handles reporting | ||
// that `while (true)` should be `loop` | ||
let UnannotatedExp_::Loop { | ||
name: _, | ||
has_break: false, | ||
body, | ||
} = &exp.exp.value | ||
else { | ||
return false; | ||
}; | ||
// TODO maybe move this to Loop? Bit of an n^2 problem here in the worst case | ||
if has_return(body) { | ||
return false; | ||
} | ||
let diag = diag!( | ||
StyleCodes::LoopWithoutExit.diag_info(), | ||
( | ||
exp.exp.loc, | ||
"'loop' without 'break' or 'return'. \ | ||
This code will until it errors, e.g. reaching an 'abort' or running out of gas" | ||
) | ||
); | ||
self.env.add_diag(diag); | ||
false | ||
} | ||
} | ||
|
||
fn has_return(e: &T::Exp) -> bool { | ||
has_special_exp(e, |e| matches!(e.exp.value, UnannotatedExp_::Return(_))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
external-crates/move/crates/move-compiler/tests/linter/false_negative_loop_without_exit.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// tests for false negatives in the loop without exit lint | ||
|
||
module a::m { | ||
public fun t1() { | ||
loop { | ||
if (false) break | ||
} | ||
} | ||
|
||
public fun t2() { | ||
loop { | ||
if (false) return | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
external-crates/move/crates/move-compiler/tests/linter/suppress_loop_without_exit.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module a::m { | ||
// Suppress Case | ||
#[allow(lint(loop_without_exit))] | ||
public fun suppressed_empty_loop() { | ||
loop {} | ||
} | ||
} |
4 changes: 2 additions & 2 deletions
4
external-crates/move/crates/move-compiler/tests/linter/suppress_unnecessary_while_loop.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
module 0x42::loop_test { | ||
module a::loop_test { | ||
|
||
#[allow(lint(while_true))] | ||
public fun suppressed_while_true() { | ||
while (true) {}; | ||
while(true) {} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
external-crates/move/crates/move-compiler/tests/linter/true_negative_loop_without_exit.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// tests for negatives in the loop without exit lint | ||
|
||
module a::m { | ||
public fun t1() { | ||
let i = 0; | ||
loop { | ||
if (i >= 10) break; | ||
i = i + 1; | ||
} | ||
} | ||
|
||
public fun t2() { | ||
let i = 0; | ||
loop { | ||
if (i >= 10) return; | ||
i = i + 1; | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
external-crates/move/crates/move-compiler/tests/linter/true_positive_loop_without_exit.exp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
warning[Lint W02006]: 'loop' without 'break' or 'return' | ||
┌─ tests/linter/true_positive_loop_without_exit.move:5:9 | ||
│ | ||
5 │ loop {} | ||
│ ^^^^^^^ 'loop' without 'break' or 'return'. This code will until it errors, e.g. reaching an 'abort' or running out of gas | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(loop_without_exit))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
||
warning[Lint W02006]: 'loop' without 'break' or 'return' | ||
┌─ tests/linter/true_positive_loop_without_exit.move:10:9 | ||
│ | ||
10 │ loop { abort ZERO } | ||
│ ^^^^^^^^^^^^^^^^^^^ 'loop' without 'break' or 'return'. This code will until it errors, e.g. reaching an 'abort' or running out of gas | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(loop_without_exit))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
||
warning[Lint W02006]: 'loop' without 'break' or 'return' | ||
┌─ tests/linter/true_positive_loop_without_exit.move:14:9 | ||
│ | ||
14 │ ╭ loop { | ||
15 │ │ t2(); | ||
16 │ │ t2(); | ||
17 │ │ } | ||
│ ╰─────────^ 'loop' without 'break' or 'return'. This code will until it errors, e.g. reaching an 'abort' or running out of gas | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(loop_without_exit))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
19 changes: 19 additions & 0 deletions
19
external-crates/move/crates/move-compiler/tests/linter/true_positive_loop_without_exit.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// tests for positives in the loop without exit lint | ||
|
||
module a::m { | ||
public fun t1() { | ||
loop {} | ||
} | ||
|
||
const ZERO: u64 = 0; | ||
public fun t2() { | ||
loop { abort ZERO } | ||
} | ||
|
||
public fun t3() { | ||
loop { | ||
t2(); | ||
t2(); | ||
} | ||
} | ||
} |