diff --git a/third_party/move/tools/move-linter/src/model_ast_lints.rs b/third_party/move/tools/move-linter/src/model_ast_lints.rs index 674003f359d3c..88ea52e5d6042 100644 --- a/third_party/move/tools/move-linter/src/model_ast_lints.rs +++ b/third_party/move/tools/move-linter/src/model_ast_lints.rs @@ -14,6 +14,7 @@ mod needless_ref_deref; mod needless_ref_in_field_access; mod nonminimal_bool; mod self_assignment; +mod simpler_bool_expression; mod simpler_numeric_expression; mod unnecessary_boolean_identity_comparison; mod unnecessary_numerical_extreme_comparison; @@ -38,6 +39,7 @@ pub fn get_default_linter_pipeline(config: &BTreeMap) -> Vec::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), Box::::default(), Box::::default(), diff --git a/third_party/move/tools/move-linter/src/model_ast_lints/simpler_bool_expression.rs b/third_party/move/tools/move-linter/src/model_ast_lints/simpler_bool_expression.rs new file mode 100644 index 0000000000000..d699bb91f44bc --- /dev/null +++ b/third_party/move/tools/move-linter/src/model_ast_lints/simpler_bool_expression.rs @@ -0,0 +1,244 @@ +// Copyright (c) Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module implements an expression linter that checks for boolean expressions +//! that can be simplified using boolean algebra laws. +//! +//! Currently detects: +//! 1. `a && b || a` which can be simplified to just `a` (absorption law) +//! 2. `a || a && b` which can be simplified to just `a` (absorption law) +//! 3. `a && a` which can be simplified to just `a` (idempotence) +//! 4. `a || a` which can be simplified to just `a` (idempotence) +//! 5. `a && !a` which can be simplified to just `false` (contradiction) +//! 6. `!a && a` which can be simplified to just `false` (contradiction) +//! 7. `a || !a` which can be simplified to just `true` (tautology) +//! 8. `!a || a` which can be simplified to just `true` (tautology) +//! 9. `(a && b) || (a && c)` which can be simplified to `a && (b || c)` (distributive law) +//! 10. `(a || b) && (a || c)` which can be simplified to `a || (b && c)` (distributive law) +//! 11. `!!a` which can be simplified to just `a` (double negation law) + +use move_compiler_v2::external_checks::ExpChecker; +use move_model::{ + ast::{Exp, ExpData, Operation}, + model::FunctionEnv, +}; +use ExpData::Call; +use Operation::{And, Not, Or}; + +enum SimplerBoolPatternType { + AbsorptionLaw, + Idempotence, + Contradiction, + Tautology, + DistributiveLaw, + DoubleNegation, +} + +impl SimplerBoolPatternType { + fn to_message(&self) -> &str { + match self { + SimplerBoolPatternType::AbsorptionLaw => "This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`).", + SimplerBoolPatternType::Idempotence => "This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`).", + SimplerBoolPatternType::Contradiction => "This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`).", + SimplerBoolPatternType::Tautology => "This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`).", + SimplerBoolPatternType::DistributiveLaw => "This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`).", + SimplerBoolPatternType::DoubleNegation => "This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`).", + } + } +} + +#[derive(Default)] +pub struct SimplerBoolExpression; + +fn is_move_function(expr: &ExpData) -> bool { + matches!(expr, ExpData::Call(_, Operation::MoveFunction(_, _), _)) +} + +fn is_constant(expr: &ExpData) -> bool { + matches!(expr, ExpData::Value(_, _)) +} + +/// Check if two expressions are structurally equal +fn is_expression_equal(expr1: &ExpData, expr2: &ExpData) -> bool { + match (expr1, expr2) { + (ExpData::LocalVar(_, s1), ExpData::LocalVar(_, s2)) => s1 == s2, + (ExpData::Value(_, v1), ExpData::Value(_, v2)) => v1 == v2, + (ExpData::Temporary(_, t1), ExpData::Temporary(_, t2)) => t1 == t2, + (ExpData::Call(_, op1, args1), ExpData::Call(_, op2, args2)) => { + if is_move_function(expr1) + || is_move_function(expr2) + || args1.iter().any(|a| is_move_function(a)) + || args2.iter().any(|a| is_move_function(a)) + { + return false; + } + + op1 == op2 + && args1.len() == args2.len() + && args1 + .iter() + .zip(args2.iter()) + .all(|(a1, a2)| is_expression_equal(a1, a2)) + }, + _ => false, + } +} + +impl SimplerBoolExpression { + /// Check for absorption law patterns: `a && b || a` or `a || a && b` + fn check_absorption_law( + &self, + left: &ExpData, + right: &ExpData, + ) -> Option { + let check_pattern = |left: &ExpData, right: &ExpData| { + if let ExpData::Call(_, And, and_args) = left { + if and_args.len() == 2 { + return is_expression_equal(and_args[0].as_ref(), right) + || is_expression_equal(and_args[1].as_ref(), right); + } + } + false + }; + + if check_pattern(left, right) { + // Pattern 1: (a && b) || a → a + Some(SimplerBoolPatternType::AbsorptionLaw) + } else if check_pattern(right, left) { + // Pattern 2: a || (a && b) → a + Some(SimplerBoolPatternType::AbsorptionLaw) + } else { + None + } + } + + /// Check for idempotence patterns: `a && a` or `a || a` + fn check_idempotence(&self, left: &ExpData, right: &ExpData) -> Option { + // If left or right is a constant or known value, we ignore this since it's already implemented in `nonminimal_bool` + if is_constant(left) || is_constant(right) { + return None; + } + + if is_expression_equal(left, right) { + return Some(SimplerBoolPatternType::Idempotence); + } + None + } + + /// Check for contradiction and tautology patterns: `a && !a` or `a || !a` + fn check_contradiction_tautology( + &self, + op: &Operation, + left: &ExpData, + right: &ExpData, + ) -> Option { + let is_negation_pair = |expr1: &ExpData, expr2: &ExpData| -> bool { + matches!(expr2, ExpData::Call(_, Not, not_args) + if not_args.len() == 1 && is_expression_equal(expr1, not_args[0].as_ref())) + }; + + // If left or right is a constant or known value, we ignore this since it's already implemented in `nonminimal_bool` + if is_constant(left) || is_constant(right) { + return None; + } + + if is_negation_pair(left, right) || is_negation_pair(right, left) { + let result_value = matches!(op, Or); + Some( + if result_value { + SimplerBoolPatternType::Tautology + } else { + SimplerBoolPatternType::Contradiction + }, + ) + } else { + None + } + } + + /// Check for distributive law patterns: `(a && b) || (a && c)` or `(a || b) && (a || c)` + fn check_distributive_law( + &self, + op: &Operation, + left: &ExpData, + right: &ExpData, + ) -> Option { + let try_distribute = + |left_args: &[Exp], right_args: &[Exp]| -> Option { + if left_args.len() != 2 || right_args.len() != 2 { + return None; + } + + for left_elem in left_args.iter() { + for right_elem in right_args.iter() { + if is_expression_equal(left_elem.as_ref(), right_elem.as_ref()) { + return Some(SimplerBoolPatternType::DistributiveLaw); + } + } + } + None + }; + + match (op, left, right) { + (Or, ExpData::Call(_, And, left_args), ExpData::Call(_, And, right_args)) + | (And, ExpData::Call(_, Or, left_args), ExpData::Call(_, Or, right_args)) => { + try_distribute(left_args, right_args) + }, + _ => None, + } + } + + /// Check for double negation pattern: `!!a` + fn check_double_negation( + &self, + op: &Operation, + args: &[Exp], + ) -> Option { + if let Not = op { + if args.len() == 1 { + if let ExpData::Call(_, Not, inner_args) = args[0].as_ref() { + if inner_args.len() == 1 { + return Some(SimplerBoolPatternType::DoubleNegation); + } + } + } + } + None + } +} + +impl ExpChecker for SimplerBoolExpression { + fn get_name(&self) -> String { + "simpler_bool_expression".to_string() + } + + fn visit_expr_pre(&mut self, function_env: &FunctionEnv<'_>, expr: &ExpData) { + let env = function_env.env(); + + let Call(id, op, args) = expr else { return }; + + let pattern = match op { + And | Or if args.len() == 2 => { + let (left, right) = (&args[0].as_ref(), &args[1].as_ref()); + + // For Or operations, try absorption law first + let absorption = if matches!(op, Or) { + self.check_absorption_law(left, right) + } else { + None + }; + + absorption + .or_else(|| self.check_idempotence(left, right)) + .or_else(|| self.check_contradiction_tautology(op, left, right)) + .or_else(|| self.check_distributive_law(op, left, right)) + }, + Not => self.check_double_negation(op, args), + _ => None, + }; + + if let Some(pattern) = pattern { + self.report(env, &env.get_node_loc(*id), pattern.to_message()); + } + } +} diff --git a/third_party/move/tools/move-linter/tests/model_ast_lints/equal_operands_in_bin_op.exp b/third_party/move/tools/move-linter/tests/model_ast_lints/equal_operands_in_bin_op.exp index d65dcc4f87d4d..55f9ceda149f1 100644 --- a/third_party/move/tools/move-linter/tests/model_ast_lints/equal_operands_in_bin_op.exp +++ b/third_party/move/tools/move-linter/tests/model_ast_lints/equal_operands_in_bin_op.exp @@ -116,3 +116,21 @@ warning: [lint] This operation always evaluates to `false`. │ = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(equal_operands_in_bin_op)]`. = For more information, see https://aptos.dev/en/build/smart-contracts/linter#equal_operands_in_bin_op. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/equal_operands_in_bin_op.move:68:13 + │ +68 │ if (b || b) { // This should trigger `simpler_boolean_expression` instead of `equal_operands_in_bin_op` + │ ^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/equal_operands_in_bin_op.move:72:13 + │ +72 │ if (b && b) { // This should trigger `simpler_boolean_expression` instead of `equal_operands_in_bin_op` + │ ^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. diff --git a/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.exp b/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.exp new file mode 100644 index 0000000000000..f6dca0c86f518 --- /dev/null +++ b/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.exp @@ -0,0 +1,676 @@ + +Diagnostics: +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:29:13 + │ +29 │ if (!!a) (); + │ ^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:36:13 + │ +36 │ if (!!a) (); + │ ^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:37:13 + │ +37 │ if (!(!b)) (); + │ ^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:43:13 + │ +43 │ if (!!flags.a) (); + │ ^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:44:13 + │ +44 │ if (!(!flags.b)) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:53:13 + │ +53 │ if (!!nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:54:13 + │ +54 │ if (!(!nested.enabled)) (); + │ ^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:62:13 + │ +62 │ if (a && b || a) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:63:13 + │ +63 │ if (a || a && b) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:70:13 + │ +70 │ if (x && y || x) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:71:13 + │ +71 │ if (x || x && y) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:75:13 + │ +75 │ if (TRUE_CONST && FALSE_CONST || TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The right-hand side of `||` evaluates to `true`. Recall that the expression `bexpr || true` is logically equivalent to `true`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:75:13 + │ +75 │ if (TRUE_CONST && FALSE_CONST || TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:75:13 + │ +75 │ if (TRUE_CONST && FALSE_CONST || TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The left-hand side of `&&` evaluates to `false`. Recall that the expression `false && bexpr` is logically equivalent to `false`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:76:28 + │ +76 │ if (FALSE_CONST || FALSE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:76:13 + │ +76 │ if (FALSE_CONST || FALSE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:76:13 + │ +76 │ if (FALSE_CONST || FALSE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:82:13 + │ +82 │ if (flags.a && flags.b || flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:83:13 + │ +83 │ if (flags.a || flags.a && flags.b) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:92:13 + │ +92 │ if (nested.flags.a && nested.enabled || nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using absorption law (`a && b || a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:93:13 + │ +93 │ if (nested.flags.a || nested.flags.a && nested.enabled) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using double negation law (`!!a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:110:13 + │ +110 │ if (!!a && !a) (); + │ ^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:110:13 + │ +110 │ if (!!a && !a) (); + │ ^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:111:13 + │ +111 │ if (a && a) (); + │ ^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:112:13 + │ +112 │ if (a || a) (); + │ ^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:118:13 + │ +118 │ if (flag && flag) (); + │ ^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:119:13 + │ +119 │ if (flag || flag) (); + │ ^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:124:13 + │ +124 │ if (TRUE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:125:13 + │ +125 │ if (FALSE_CONST || FALSE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:131:13 + │ +131 │ if (flags.a && flags.a) (); + │ ^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:132:13 + │ +132 │ if (flags.b || flags.b) (); + │ ^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:141:13 + │ +141 │ if (nested.flags.a && nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using idempotence (`a && a` is equivalent to `a`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:142:13 + │ +142 │ if (nested.flags.b || nested.flags.b) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:160:13 + │ +160 │ if (a && !a) (); + │ ^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:161:13 + │ +161 │ if (!a && a) (); + │ ^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:162:13 + │ +162 │ if (a || !a) (); + │ ^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:163:13 + │ +163 │ if (!a || a) (); + │ ^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:169:13 + │ +169 │ if (condition && !condition) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:170:13 + │ +170 │ if (!condition && condition) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:171:13 + │ +171 │ if (condition || !condition) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:172:13 + │ +172 │ if (!condition || condition) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This expression evaluates to `false`. Recall that the expression `!true` is logically equivalent to `false`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:177:27 + │ +177 │ if (TRUE_CONST && !TRUE_CONST) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:177:13 + │ +177 │ if (TRUE_CONST && !TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This expression evaluates to `false`. Recall that the expression `!true` is logically equivalent to `false`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:178:13 + │ +178 │ if (!TRUE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The right-hand side of `&&` evaluates to `true`. Recall that the expression `bexpr && true` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:178:13 + │ +178 │ if (!TRUE_CONST && TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This expression evaluates to `true`. Recall that the expression `!false` is logically equivalent to `true`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:179:28 + │ +179 │ if (FALSE_CONST || !FALSE_CONST) (); + │ ^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:179:13 + │ +179 │ if (FALSE_CONST || !FALSE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This expression evaluates to `true`. Recall that the expression `!false` is logically equivalent to `true`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:180:13 + │ +180 │ if (!FALSE_CONST || FALSE_CONST) (); + │ ^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The right-hand side of `||` evaluates to `false`. Recall that the expression `bexpr || false` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:180:13 + │ +180 │ if (!FALSE_CONST || FALSE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:186:13 + │ +186 │ if (flags.a && !flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:187:13 + │ +187 │ if (!flags.a && flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:188:13 + │ +188 │ if (flags.a || !flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:189:13 + │ +189 │ if (!flags.a || flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:198:13 + │ +198 │ if (nested.flags.a && !nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using contradiction (`a && !a` is equivalent to `false`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:199:13 + │ +199 │ if (!nested.flags.a && nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:200:13 + │ +200 │ if (nested.flags.a || !nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using tautology (`a || !a` is equivalent to `true`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:201:13 + │ +201 │ if (!nested.flags.a || nested.flags.a) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:219:13 + │ +219 │ if ((a && b) || (a && c)) (); + │ ^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:220:13 + │ +220 │ if ((a || b) && (a || c)) (); + │ ^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:228:13 + │ +228 │ if ((a && b) || (a && c)) (); + │ ^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:229:13 + │ +229 │ if ((a || b) && (a || c)) (); + │ ^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:233:13 + │ +233 │ if ((TRUE_CONST && FALSE_CONST) || (TRUE_CONST && TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:233:44 + │ +233 │ if ((TRUE_CONST && FALSE_CONST) || (TRUE_CONST && TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:233:13 + │ +233 │ if ((TRUE_CONST && FALSE_CONST) || (TRUE_CONST && TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:234:13 + │ +234 │ if ((FALSE_CONST || FALSE_CONST) && (FALSE_CONST || TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:234:45 + │ +234 │ if ((FALSE_CONST || FALSE_CONST) && (FALSE_CONST || TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:234:13 + │ +234 │ if ((FALSE_CONST || FALSE_CONST) && (FALSE_CONST || TRUE_CONST)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:241:13 + │ +241 │ if ((flags.a && other.a) || (flags.a && other.c)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:242:13 + │ +242 │ if ((flags.a || other.a) && (flags.a || other.c)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:251:13 + │ +251 │ if ((nested.flags.a && nested.enabled) || (nested.flags.a && nested.flags.c)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] This boolean expression can be simplified using distributive law (`(a && b) || (a && c)` is equivalent to `a && (b || c)`). + ┌─ tests/model_ast_lints/simpler_bool_expression.move:252:13 + │ +252 │ if ((nested.flags.a || nested.enabled) && (nested.flags.a || nested.flags.c)) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(simpler_bool_expression)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#simpler_bool_expression. + +warning: [lint] The right-hand side of `&&` evaluates to `true`. Recall that the expression `bexpr && true` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:259:13 + │ +259 │ if ((helper_function() && TRUE_CONST) || (helper_function() && (y > 10))) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The right-hand side of `||` evaluates to `false`. Recall that the expression `bexpr || false` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:260:13 + │ +260 │ if ((helper_function() || FALSE_CONST) && (helper_function() || (y > 10))) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `&&` evaluates to `true`. Recall that the expression `true && bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:292:13 + │ +292 │ if (TRUE_CONST && FALSE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. + +warning: [lint] The left-hand side of `||` evaluates to `false`. Recall that the expression `false || bexpr` is logically equivalent to `bexpr`. Consider simplifying. + ┌─ tests/model_ast_lints/simpler_bool_expression.move:293:13 + │ +293 │ if (FALSE_CONST || TRUE_CONST) (); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + = To suppress this warning, annotate the function/module with the attribute `#[lint::skip(nonminimal_bool)]`. + = For more information, see https://aptos.dev/en/build/smart-contracts/linter#nonminimal_bool. diff --git a/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.move b/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.move new file mode 100644 index 0000000000000..d903623ba4357 --- /dev/null +++ b/third_party/move/tools/move-linter/tests/model_ast_lints/simpler_bool_expression.move @@ -0,0 +1,334 @@ +module 0xc0ffee::m { + + const TRUE_CONST: bool = true; + const FALSE_CONST: bool = false; + + struct BoolFlags has copy, drop { + a: bool, + b: bool, + c: bool, + } + + struct NestedStruct has copy, drop { + flags: BoolFlags, + enabled: bool, + } + + public fun helper_function(): bool { + true + } + + public fun get_bool_flags(): BoolFlags { + BoolFlags { a: true, b: false, c: true } + } + + // ===== DOUBLE NEGATION LAW TESTS (should trigger lint) ===== + // Pattern: !!a should simplify to just a + + public fun test_double_negation_parameters(a: bool) { + if (!!a) (); + } + + public fun test_double_negation_variables() { + let a = true; + let b = false; + + if (!!a) (); + if (!(!b)) (); + } + + public fun test_double_negation_struct_field() { + let flags = get_bool_flags(); + + if (!!flags.a) (); + if (!(!flags.b)) (); + } + + public fun test_double_negation_nested_struct() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if (!!nested.flags.a) (); + if (!(!nested.enabled)) (); + } + + // ===== ABSORPTION LAW TESTS (should trigger lint) ===== + // Pattern: a && b || a should simplify to just a + // Pattern: a || a && b should simplify to just a + + public fun test_absorption_law_parameters(a: bool, b: bool) { + if (a && b || a) (); + if (a || a && b) (); + } + + public fun test_absorption_law_variables() { + let x = true; + let y = false; + + if (x && y || x) (); + if (x || x && y) (); + } + + public fun test_absorption_law_constants() { + if (TRUE_CONST && FALSE_CONST || TRUE_CONST) (); + if (FALSE_CONST || FALSE_CONST && TRUE_CONST) (); + } + + public fun test_absorption_law_struct_field() { + let flags = get_bool_flags(); + + if (flags.a && flags.b || flags.a) (); + if (flags.a || flags.a && flags.b) (); + } + + public fun test_absorption_law_nested_struct() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if (nested.flags.a && nested.enabled || nested.flags.a) (); + if (nested.flags.a || nested.flags.a && nested.enabled) (); + } + + // This test should not trigger the lint, since helper_function() might have side effects + public fun test_absorption_law_mixed() { + let x = true; + let p = 20; + + if ((helper_function()) && p > 10 || helper_function()) (); + if ((x == helper_function()) || x == helper_function() && p > 10) (); + } + + // ===== IDEMPOTENCE LAW TESTS (should trigger lint) ===== + // Pattern: a && a should simplify to just a + // Pattern: a || a should simplify to just a + + public fun test_idempotence_parameters(a: bool) { + if (!!a && !a) (); + if (a && a) (); + if (a || a) (); + } + + public fun test_idempotence_variables() { + let flag = true; + + if (flag && flag) (); + if (flag || flag) (); + } + + // This test should not trigger the lint, since it's already implemented in `nonminimal_bool` + public fun test_idempotence_constants() { + if (TRUE_CONST && TRUE_CONST) (); + if (FALSE_CONST || FALSE_CONST) (); + } + + public fun test_idempotence_struct_field() { + let flags = get_bool_flags(); + + if (flags.a && flags.a) (); + if (flags.b || flags.b) (); + } + + public fun test_idempotence_nested_struct() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if (nested.flags.a && nested.flags.a) (); + if (nested.flags.b || nested.flags.b) (); + } + + // This test should not trigger the lint, since helper_function() might have side effects + public fun test_idempotence_mixed() { + let x = true; + + if ((x == helper_function()) && (x == helper_function())) (); + if ((x == helper_function()) || (x == helper_function())) (); + } + + // ===== CONTRADICTION TAUTOLOGY TESTS (should trigger lint) ===== + // Pattern: a && !a should simplify to just false + // Pattern: !a && a should simplify to just false + // Pattern: a || !a should simplify to just true + // Pattern: !a || a should simplify to just true + + public fun test_contradiction_tautology_parameters(a: bool) { + if (a && !a) (); + if (!a && a) (); + if (a || !a) (); + if (!a || a) (); + } + + public fun test_contradiction_tautology_variables() { + let condition = true; + + if (condition && !condition) (); + if (!condition && condition) (); + if (condition || !condition) (); + if (!condition || condition) (); + } + + // This test should not trigger the lint, since it's already implemented in `nonminimal_bool` + public fun test_contradiction_tautology_constants() { + if (TRUE_CONST && !TRUE_CONST) (); + if (!TRUE_CONST && TRUE_CONST) (); + if (FALSE_CONST || !FALSE_CONST) (); + if (!FALSE_CONST || FALSE_CONST) (); + } + + public fun test_contradiction_tautology_struct_field() { + let flags = get_bool_flags(); + + if (flags.a && !flags.a) (); + if (!flags.a && flags.a) (); + if (flags.a || !flags.a) (); + if (!flags.a || flags.a) (); + } + + public fun test_contradiction_tautology_nested_struct() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if (nested.flags.a && !nested.flags.a) (); + if (!nested.flags.a && nested.flags.a) (); + if (nested.flags.a || !nested.flags.a) (); + if (!nested.flags.a || nested.flags.a) (); + } + + // This test should not trigger the lint, since helper_function() might have side effects + public fun test_contradiction_tautology_mixed() { + let x = true; + + if ((x == helper_function()) && !(x == helper_function())) (); + if (!(x == helper_function()) && (x == helper_function())) (); + if ((x == helper_function()) || !(x == helper_function())) (); + if (!(x == helper_function()) || (x == helper_function())) (); + } + + // ===== DISTRIBUTIVE LAW TESTS (should trigger lint) ===== + // Pattern: (a && b) || (a && c) should simplify to a && (b || c) + // Pattern: (a || b) && (a || c) should simplify to a || (b && c) + + public fun test_distributive_law_parameters(a: bool, b: bool, c: bool) { + if ((a && b) || (a && c)) (); + if ((a || b) && (a || c)) (); + } + + public fun test_distributive_law_variables() { + let a = true; + let b = false; + let c = true; + + if ((a && b) || (a && c)) (); + if ((a || b) && (a || c)) (); + } + + public fun test_distributive_law_constants() { + if ((TRUE_CONST && FALSE_CONST) || (TRUE_CONST && TRUE_CONST)) (); + if ((FALSE_CONST || FALSE_CONST) && (FALSE_CONST || TRUE_CONST)) (); + } + + public fun test_distributive_law_struct_field() { + let flags = get_bool_flags(); + let other = BoolFlags { a: false, b: true, c: false }; + + if ((flags.a && other.a) || (flags.a && other.c)) (); + if ((flags.a || other.a) && (flags.a || other.c)) (); + } + + public fun test_distributive_law_nested_struct() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if ((nested.flags.a && nested.enabled) || (nested.flags.a && nested.flags.c)) (); + if ((nested.flags.a || nested.enabled) && (nested.flags.a || nested.flags.c)) (); + } + + // This test should not trigger the lint, since helper_function() might have side effects + public fun test_distributive_law_mixed() { + let y = 5; + + if ((helper_function() && TRUE_CONST) || (helper_function() && (y > 10))) (); + if ((helper_function() || FALSE_CONST) && (helper_function() || (y > 10))) (); + } + + + // ===== LINT SKIP TESTS ===== + + #[lint::skip(simpler_bool_expression)] + public fun test_skipped_simplifiable() { + let a = true; + let b = false; + + if (a && a) (); + if (b || !b) (); + if (a && !a) (); + } + + // ===== NEGATIVE TESTS (should NOT trigger lint) ===== + + public fun test_parameters_no_lint(a: bool, b: bool, c: bool) { + if (a && b || c) (); + if (c || a && b) (); + } + + public fun test_variables_no_lint() { + let a = true; + let b = false; + + if (a && b) (); + if (b || a) (); + } + + public fun test_constants_no_lint() { + if (TRUE_CONST && FALSE_CONST) (); + if (FALSE_CONST || TRUE_CONST) (); + } + + public fun test_different_struct_fields_no_lint() { + let flags = BoolFlags { a: true, b: false, c: true }; + + if (flags.a && flags.b) (); + if (flags.a || flags.c) (); + if (!flags.a && flags.b) (); + if (flags.b && !flags.c) (); + } + + public fun test_nested_struct_no_lint() { + let nested = NestedStruct { + flags: BoolFlags { a: true, b: false, c: true }, + enabled: true + }; + + if (nested.flags.a && nested.enabled) (); + if (nested.flags.a || nested.enabled) (); + if (!nested.flags.a && nested.enabled) (); + if (nested.flags.a && !nested.enabled) (); + } + + public fun test_function_call_no_lint() { + let x = helper_function(); + let y = helper_function(); + + if (x && y) (); + if (x || y) (); + } + + // ===== THE FOLLOWING TEST DOES NOT TRIGGER THE LINT, BUT IS INCLUDED FOR FUTURE REFERENCE ===== + // Detecting these patterns is not yet supported. + + public fun test_vector_no_lint() { + let new_vec = vector[true, false]; + + if (new_vec[0] && new_vec[0]) (); + if (new_vec[1] || new_vec[1]) (); + } +}