forge_lint/sol/med/
div_mul.rs

1use 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}