Skip to content

Commit

Permalink
[move][move-linter] implemen meaningless math operator rules
Browse files Browse the repository at this point in the history
  • Loading branch information
tx-tomcat committed May 31, 2024
1 parent 67d6d0d commit 4098c56
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ use crate::{
},
};

use super::{LinterDiagCategory, CONSTANT_NAMING_DIAG_CODE, LINT_WARNING_PREFIX};
use super::{LinterDiagnosticCategory, CONSTANT_NAMING_DIAG_CODE, LINT_WARNING_PREFIX};

/// Diagnostic information for constant naming violations.
const CONSTANT_NAMING_DIAG: DiagnosticInfo = custom(
LINT_WARNING_PREFIX,
Severity::Warning,
LinterDiagCategory::Style as u8,
LinterDiagnosticCategory::Style as u8,
CONSTANT_NAMING_DIAG_CODE,
"constant should follow naming convention",
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Detects meaningless math operations like `x * 0`, `x << 0`, `x >> 0`, `x * 1`, `x + 0`, `x - 0`, and `x / 0`.
//! Aims to reduce code redundancy and improve clarity by flagging operations with no effect.
use crate::{
diag,
diagnostics::{
codes::{custom, DiagnosticInfo, Severity},
WarningFilters,
},
expansion::ast::Value_,
parser::ast::BinOp_,
shared::CompilationEnv,
typing::{
ast::{self as T, Exp, UnannotatedExp_},
visitor::{TypingVisitorConstructor, TypingVisitorContext},
},
};
use move_ir_types::location::Loc;

use super::{LinterDiagnosticCategory, LINT_WARNING_PREFIX, MEANINGLESS_MATH_OP_DIAG_CODE};

const MEANINGLESS_MATH_OP_DIAG: DiagnosticInfo = custom(
LINT_WARNING_PREFIX,
Severity::Warning,
LinterDiagnosticCategory::Complexity as u8,
MEANINGLESS_MATH_OP_DIAG_CODE,
"Detected a meaningless math operation that has no effect.",
);

pub struct MeaninglessMathOperation;

pub struct Context<'a> {
env: &'a mut CompilationEnv,
}

impl TypingVisitorConstructor for MeaninglessMathOperation {
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 visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool {
if let UnannotatedExp_::BinopExp(_, op, _, rhs) = &exp.exp.value {
match op.value {
BinOp_::Mul | BinOp_::Div => {
if is_zero(&rhs) || (matches!(op.value, BinOp_::Mul) && is_one(&rhs)) {
report_meaningless_math_op(self.env, op.loc);
}
}
BinOp_::Add | BinOp_::Sub => {
if is_zero(&rhs) {
report_meaningless_math_op(self.env, op.loc);
}
}
BinOp_::Shl | BinOp_::Shr => {
if is_zero(&rhs) {
report_meaningless_math_op(self.env, op.loc);
}
}
_ => {}
}
}

false
}
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 is_zero(exp: &Exp) -> bool {
if let UnannotatedExp_::Value(spanned) = &exp.exp.value {
matches!(spanned.value, Value_::U64(0))
} else {
false
}
}

fn is_one(exp: &Exp) -> bool {
if let UnannotatedExp_::Value(spanned) = &exp.exp.value {
matches!(spanned.value, Value_::U64(1))
} else {
false
}
}

fn report_meaningless_math_op(env: &mut CompilationEnv, loc: Loc) {
let diag = diag!(
MEANINGLESS_MATH_OP_DIAG,
(
loc,
"Detected a meaningless math operation that has no effect.",
)
);
env.add_diag(diag);
}
44 changes: 28 additions & 16 deletions external-crates/move/crates/move-compiler/src/linters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

use move_symbol_pool::Symbol;

use crate::{
command_line::compiler::Visitor, diagnostics::codes::WarningFilter,
linters::constant_naming::ConstantNamingVisitor, typing::visitor::TypingVisitor,
linters::constant_naming::ConstantNamingVisitor,
linters::meaningless_math_operation::MeaninglessMathOperation, typing::visitor::TypingVisitor,
};
use move_symbol_pool::Symbol;

pub mod constant_naming;
pub mod meaningless_math_operation;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LintLevel {
// No linters
Expand All @@ -32,22 +35,28 @@ pub enum LinterDiagnosticCategory {
pub const ALLOW_ATTR_CATEGORY: &str = "lint";
pub const LINT_WARNING_PREFIX: &str = "Lint ";
pub const CONSTANT_NAMING_FILTER_NAME: &str = "constant_naming";

pub const CONSTANT_NAMING_DIAG_CODE: u8 = 1;

pub enum LinterDiagCategory {
Style,
}
pub const MEANINGLESS_MATH_OP_FILTER_NAME: &str = "meaningless_math_op";
pub const MEANINGLESS_MATH_OP_DIAG_CODE: u8 = 6;

pub fn known_filters() -> (Option<Symbol>, Vec<WarningFilter>) {
(
Some(ALLOW_ATTR_CATEGORY.into()),
vec![WarningFilter::code(
Some(LINT_WARNING_PREFIX),
LinterDiagCategory::Style as u8,
CONSTANT_NAMING_DIAG_CODE,
Some(CONSTANT_NAMING_FILTER_NAME),
)],
vec![
WarningFilter::code(
Some(LINT_WARNING_PREFIX),
LinterDiagnosticCategory::Style as u8,
CONSTANT_NAMING_DIAG_CODE,
Some(CONSTANT_NAMING_FILTER_NAME),
),
WarningFilter::code(
Some(LINT_WARNING_PREFIX),
LinterDiagnosticCategory::Complexity as u8,
MEANINGLESS_MATH_OP_DIAG_CODE,
Some(MEANINGLESS_MATH_OP_FILTER_NAME),
),
],
)
}

Expand All @@ -56,9 +65,12 @@ pub fn linter_visitors(level: LintLevel) -> Vec<Visitor> {
LintLevel::None => vec![],
LintLevel::Default => vec![],
LintLevel::All => {
vec![constant_naming::ConstantNamingVisitor::visitor(
ConstantNamingVisitor,
)]
vec![
constant_naming::ConstantNamingVisitor::visitor(ConstantNamingVisitor),
meaningless_math_operation::MeaninglessMathOperation::visitor(
MeaninglessMathOperation,
),
]
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@ use crate::{
};
use move_ir_types::location::Loc;
use move_symbol_pool::Symbol;

pub mod coin_field;
pub mod collection_equality;
pub mod custom_state_change;
pub mod freeze_wrapped;
pub mod public_random;
pub mod self_transfer;
pub mod share_owned;

pub const SUI_PKG_NAME: &str = "sui";
pub const INCLUDE_NEW_RULES: bool = true;

pub const TRANSFER_MOD_NAME: &str = "transfer";
pub const TRANSFER_FUN: &str = "transfer";
Expand Down Expand Up @@ -87,7 +86,7 @@ pub enum LinterDiagnosticCode {
}

pub fn known_filters() -> (Option<Symbol>, Vec<WarningFilter>) {
let filters = vec![
let mut filters = vec![
WarningFilter::All(Some(LINT_WARNING_PREFIX)),
WarningFilter::code(
Some(LINT_WARNING_PREFIX),
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module 0x42::M {
// Intentionally meaningful operations for control cases
public fun multiply(x: u64, y: u64): u64 {
x * y // This should not trigger the linter
}

public fun divide_by_nonzero(x: u64, y: u64): u64 {
x / y // Assuming y is not zero, this should not trigger the linter
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
warning[Lint W00001]: constant should follow naming convention
warning[Lint W04001]: constant should follow naming convention
┌─ tests/linter/incorrect_constant_naming.move:3:5
3 │ const Another_BadName: u64 = 42; // Should trigger a warning
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 'Another_BadName' should be ALL_CAPS. Or for error constants, use PascalCase
= This warning can be suppressed with '#[allow(lint(constant_naming))]' applied to the 'module' or module member ('const', 'fun', or 'struct')

warning[Lint W00001]: constant should follow naming convention
warning[Lint W04001]: constant should follow naming convention
┌─ tests/linter/incorrect_constant_naming.move:4:5
4 │ const JSON_Max_Size: u64 = 1048576;
Expand Down
Loading

0 comments on commit 4098c56

Please sign in to comment.