-
Notifications
You must be signed in to change notification settings - Fork 11.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Linter] Combinable bool conditions #16479
[Linter] Combinable bool conditions #16479
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
4 Skipped Deployments
|
|
||
impl TypingVisitorContext for Context<'_> { | ||
fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { | ||
if let UnannotatedExp_::BinopExp(e1, _op, _, e2) = &exp.exp.value { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this rule really works as is. Few flaws
- We don't consider the
_op
here (wouldn't work really for &&)
Bigger flaw - We don't consider the left/right operands for
op1
andop2. For example, you couldn't combine
x < y || y == zinto
x <= y`.
The overall structure here is okay, but it will need a lot of work to come up with a good heuristic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conceptually this lint has some flaws by not checking for the same e1
and e2
between comparison operations.
Otherwise we would erroneously report errors for x > y && y != z
|
||
impl TypingVisitorContext for Context<'_> { | ||
fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { | ||
if let UnannotatedExp_::BinopExp(e1, _op, _, e2) = &exp.exp.value { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to check for &&
here, yeah?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or a different set of rules for ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implemented
external-crates/move/crates/move-compiler/src/linters/combinable_bool_conditions.rs
Outdated
Show resolved
Hide resolved
if let UnannotatedExp_::BinopExp(e1, op, _, e2) = &exp.exp.value { | ||
if let ( | ||
UnannotatedExp_::BinopExp(lhs1, op1, _, rhs1), | ||
UnannotatedExp_::BinopExp(lhs2, op2, _, rhs2), | ||
) = (&e1.exp.value, &e2.exp.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's switch these to let-else
to reduce nesting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
) = (&e1.exp.value, &e2.exp.value) | ||
{ | ||
// Check both exp side are the same | ||
if lhs1 == lhs2 && rhs1 == rhs2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this guard will incorrectly catch this code:
let mut v = make_vector();
if (v.pop_back() == 1 && v.pop_back() == 1) { .. }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch! Yeah this will not work for any function call, or any operation that has side effects, for example
{ let c = x; x = true; c } && { let c = x; x = true; c }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its difficult to determine the effect of functions call so that I ignored the module call case.
exp.exp.loc, | ||
"This is always contradictory and can be simplified to false", | ||
); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about where op.value
is ==
or !=
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that ==
AND !=
or ==
OR !=
?
if op.value == BinOp_::And { | ||
add_replaceable_comparison_diag( | ||
self.env, | ||
exp.exp.loc, | ||
"This is always contradictory and can be simplified to false", | ||
); | ||
} else { | ||
add_replaceable_comparison_diag( | ||
self.env, | ||
exp.exp.loc, | ||
"Consider simplifying to `<=`.", | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd combine these branches to something like
let msg = if op.value == BinOp_::And { ... } else { ... };
add_replaceable_comparison_diag(self.env, exp.exp.loc, msg)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
(BinOp_::Neq, BinOp_::Lt) | (BinOp_::Lt, BinOp_::Neq) => { | ||
if op.value == BinOp_::And { | ||
add_replaceable_comparison_diag( | ||
self.env, | ||
exp.exp.loc, | ||
"Consider simplifying to `<`.", | ||
); | ||
} | ||
} | ||
(BinOp_::Neq, BinOp_::Gt) | (BinOp_::Gt, BinOp_::Neq) => { | ||
if op.value == BinOp_::And { | ||
add_replaceable_comparison_diag( | ||
self.env, | ||
exp.exp.loc, | ||
"Consider simplifying to `>`.", | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These cases feel a bit different. It is not really "simplifying" rather the !=
is already covered by the GT and LT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
use crate::{command_line::compiler::Visitor, diagnostics::codes::WarningFilter}; | ||
|
||
use crate::{ | ||
command_line::compiler::Visitor, diagnostics::codes::WarningFilter, | ||
linters::combinable_bool_conditions::CombinableBool, typing::visitor::TypingVisitor, | ||
}; | ||
pub mod combinable_bool_conditions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will have be redone on main
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
if (x == y || z < y) {}; | ||
if (x < y || x == y) {}; // should be x <= y | ||
if (x == y || x > y) {}; // should be x >= y | ||
if (x > y || x == y) {}; // should be x >= y | ||
if (x != y || x < y) {}; // same as x < y | ||
if (x < y || x != y) {}; // same as x < y | ||
if (x != y || x > y) {}; // same as x > y | ||
if (x > y || x != y) {}; // same as x > y | ||
if (x > y || y != x) {}; // same as x > y | ||
if (x > y && y == x) {}; // same as x >= y | ||
if (m == n || m < n) {}; // should be m <= n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably have some tests that don't involve if
env: &'a mut CompilationEnv, | ||
} | ||
|
||
impl TypingVisitorConstructor for CombinableBool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move this to the CFGIR to take advantage of constant folding and inlining
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will need to setup a non-absint visitor for you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that mean you setup visitor for us or we do it by ourself?
@@ -0,0 +1,25 @@ | |||
module 0x42::M { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow the pattern for tests we arrived at for the constant naming
- Correct
linter
dir - Separate positive/negative tests
- a case to the allow(lint(_)) case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
@@ -17,16 +20,33 @@ pub enum LintLevel { | |||
|
|||
pub const ALLOW_ATTR_CATEGORY: &str = "lint"; | |||
pub const LINT_WARNING_PREFIX: &str = "Lint "; | |||
pub const COMBINABLE_BOOL_FILTER_NAME: &str = "combinable_bool_conditions"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub const COMBINABLE_BOOL_FILTER_NAME: &str = "combinable_bool_conditions"; | |
pub const COMBINABLE_COMPARISON_FILTER_NAME: &str = "combinable_comparison"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored
@dzungdinh94 is attempting to deploy a commit to the Mysten Labs Team on Vercel. A member of the Team first needs to authorize it. |
Description
The rules detects and warns about comparison operations in Move code that can be simplified. It identifies comparisons over the same operands joined with a boolean operation, and suggests how to collapse them to a single operation.
Testing
Release notes