-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[move][move-linter] implement unnecessary while loop rules
- Loading branch information
Showing
12 changed files
with
183 additions
and
152 deletions.
There are no files selected for viewing
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
109 changes: 0 additions & 109 deletions
109
external-crates/move/crates/move-compiler/src/linters/shift_overflow.rs
This file was deleted.
Oops, something went wrong.
72 changes: 72 additions & 0 deletions
72
external-crates/move/crates/move-compiler/src/linters/unnecessary_while_loop.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,72 @@ | ||
//! Encourages replacing `while(true)` with `loop` for infinite loops in Move for clarity and conciseness. | ||
//! Identifies `while(true)` patterns, suggesting a more idiomatic approach using `loop`. | ||
//! Aims to enhance code readability and adherence to Rust idioms. | ||
use crate::{ | ||
diag, | ||
diagnostics::{ | ||
codes::{custom, DiagnosticInfo, Severity}, | ||
WarningFilters, | ||
}, | ||
expansion::ast::Value_, | ||
shared::CompilationEnv, | ||
typing::{ | ||
ast::{self as T, UnannotatedExp_}, | ||
visitor::{TypingVisitorConstructor, TypingVisitorContext}, | ||
}, | ||
}; | ||
use move_ir_types::location::Loc; | ||
|
||
use super::{LinterDiagnosticCategory, LINT_WARNING_PREFIX, WHILE_TRUE_TO_LOOP_DIAG_CODE}; | ||
|
||
const WHILE_TRUE_TO_LOOP_DIAG: DiagnosticInfo = custom( | ||
LINT_WARNING_PREFIX, | ||
Severity::Warning, | ||
LinterDiagnosticCategory::Complexity as u8, | ||
WHILE_TRUE_TO_LOOP_DIAG_CODE, | ||
"Detected `while(true) {}` loop. Consider replacing with `loop {}`", | ||
); | ||
|
||
pub struct WhileTrueToLoop; | ||
|
||
pub struct Context<'a> { | ||
env: &'a mut CompilationEnv, | ||
} | ||
|
||
impl TypingVisitorConstructor for WhileTrueToLoop { | ||
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: &mut T::Exp) -> bool { | ||
if let UnannotatedExp_::While(_, cond, _) = &exp.exp.value { | ||
if is_condition_always_true(&cond.exp.value) { | ||
let diag = diag!( | ||
WHILE_TRUE_TO_LOOP_DIAG, | ||
(exp.exp.loc, "`while (true)` can be replaced with `loop`") | ||
); | ||
self.env.add_diag(diag); | ||
} | ||
} | ||
false | ||
} | ||
} | ||
|
||
fn is_condition_always_true(condition: &UnannotatedExp_) -> bool { | ||
if let UnannotatedExp_::Value(val) = condition { | ||
if let Value_::Bool(b) = &val.value { | ||
return *b; | ||
} | ||
} | ||
false | ||
} |
24 changes: 0 additions & 24 deletions
24
external-crates/move/crates/move-compiler/tests/custom_rules/shift_overflow.exp
This file was deleted.
Oops, something went wrong.
9 changes: 0 additions & 9 deletions
9
external-crates/move/crates/move-compiler/tests/custom_rules/shift_overflow.move
This file was deleted.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
...-crates/move/crates/move-compiler/tests/linter/false_negative_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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module 0x42::loop_test { | ||
|
||
public fun false_negative_obfuscated_true() { | ||
let always_true = true; | ||
while (always_true) { | ||
// This should trigger the linter but might not due to indirection | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...-crates/move/crates/move-compiler/tests/linter/false_positive_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 |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module 0x42::loop_test { | ||
|
||
public fun false_positive_complex_condition() { | ||
while (complex_always_true_condition()) { | ||
// This might trigger the linter if the condition is too complex to analyze | ||
} | ||
} | ||
|
||
fun complex_always_true_condition(): bool { | ||
true | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
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 |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module 0x42::loop_test { | ||
|
||
#[allow(lint(while_true_to_loop))] | ||
public fun suppressed_while_true() { | ||
while (true) { | ||
// This loop will run forever, but won't trigger the linter warning | ||
if (false) { | ||
break | ||
} | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...l-crates/move/crates/move-compiler/tests/linter/true_negative_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 |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module 0x42::loop_test { | ||
|
||
// True Negative Cases | ||
// These should not trigger the linter warning | ||
public fun true_negative_correct_infinite_loop() { | ||
loop { | ||
// This is the correct way to write an infinite loop | ||
} | ||
} | ||
|
||
public fun true_negative_while_with_condition(n: u64) { | ||
let i = 0; | ||
while (i < n) { | ||
i = i + 1; | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...al-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.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,21 @@ | ||
warning[Lint W01004]: Detected `while(true) {}` loop. Consider replacing with `loop {}` | ||
┌─ tests/linter/true_positive_unnecessary_while_loop.move:4:9 | ||
│ | ||
4 │ ╭ while (true) { | ||
5 │ │ // This should trigger the linter | ||
6 │ │ } | ||
│ ╰─────────^ `while (true)` can be replaced with `loop` | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(while_true_to_loop))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
||
warning[Lint W01004]: Detected `while(true) {}` loop. Consider replacing with `loop {}` | ||
┌─ tests/linter/true_positive_unnecessary_while_loop.move:11:9 | ||
│ | ||
11 │ ╭ while (true) { | ||
12 │ │ if (i == 10) break; | ||
13 │ │ i = i + 1; | ||
14 │ │ } | ||
│ ╰─────────^ `while (true)` can be replaced with `loop` | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(while_true_to_loop))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
16 changes: 16 additions & 0 deletions
16
...l-crates/move/crates/move-compiler/tests/linter/true_positive_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 |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module 0x42::loop_test { | ||
// This function should trigger the linter | ||
public fun true_positive_infinite_loop() { | ||
while (true) { | ||
// This should trigger the linter | ||
} | ||
} | ||
|
||
public fun true_positive_finite_loop() { | ||
let i = 0; | ||
while (true) { | ||
if (i == 10) break; | ||
i = i + 1; | ||
} | ||
} | ||
} |
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