forge_lint/sol/med/
div_mul.rs1use super::DivideBeforeMultiply;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::{BinOp, BinOpKind, Expr, ExprKind},
8 interface::SpannedOption,
9};
10
11declare_forge_lint!(
12 DIVIDE_BEFORE_MULTIPLY,
13 Severity::Med,
14 "divide-before-multiply",
15 "multiplication should occur before division to avoid loss of precision"
16);
17
18impl<'ast> EarlyLintPass<'ast> for DivideBeforeMultiply {
19 fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
20 if let ExprKind::Binary(left_expr, BinOp { kind: BinOpKind::Mul, .. }, _) = &expr.kind
21 && contains_division(left_expr)
22 {
23 ctx.emit(&DIVIDE_BEFORE_MULTIPLY, expr.span);
24 }
25 }
26}
27
28fn contains_division<'ast>(expr: &'ast Expr<'ast>) -> bool {
29 match &expr.kind {
30 ExprKind::Binary(_, BinOp { kind: BinOpKind::Div, .. }, _) => true,
31 ExprKind::Tuple(inner_exprs) => inner_exprs.iter().any(|opt_expr| {
32 if let SpannedOption::Some(inner_expr) = opt_expr.as_ref() {
33 contains_division(inner_expr)
34 } else {
35 false
36 }
37 }),
38 _ => false,
39 }
40}