Skip to main content

forge_lint/sol/high/
incorrect_shift.rs

1use super::IncorrectShift;
2use crate::{
3    linter::{EarlyLintPass, LintContext},
4    sol::{Severity, SolLint},
5};
6use solar::{
7    ast::{Stmt, StmtKind, yul},
8    interface::kw,
9};
10
11declare_forge_lint!(
12    INCORRECT_SHIFT,
13    Severity::High,
14    "incorrect-shift",
15    "the order of args in a shift operation is incorrect"
16);
17
18impl<'ast> EarlyLintPass<'ast> for IncorrectShift {
19    fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
20        if let StmtKind::Assembly(assembly) = &stmt.kind {
21            check_yul_block(ctx, &assembly.block);
22        }
23    }
24}
25
26fn check_yul_block(ctx: &LintContext, block: &yul::Block<'_>) {
27    for stmt in block.stmts.iter() {
28        check_yul_stmt(ctx, stmt);
29    }
30}
31
32fn check_yul_stmt(ctx: &LintContext, stmt: &yul::Stmt<'_>) {
33    match &stmt.kind {
34        yul::StmtKind::Block(block) => check_yul_block(ctx, block),
35        yul::StmtKind::AssignSingle(_, expr)
36        | yul::StmtKind::AssignMulti(_, expr)
37        | yul::StmtKind::Expr(expr) => check_yul_expr(ctx, expr),
38        yul::StmtKind::If(cond, block) => {
39            check_yul_expr(ctx, cond);
40            check_yul_block(ctx, block);
41        }
42        yul::StmtKind::For(for_stmt) => {
43            check_yul_block(ctx, &for_stmt.init);
44            check_yul_expr(ctx, &for_stmt.cond);
45            check_yul_block(ctx, &for_stmt.step);
46            check_yul_block(ctx, &for_stmt.body);
47        }
48        yul::StmtKind::Switch(switch) => {
49            check_yul_expr(ctx, &switch.selector);
50            for case in switch.cases.iter() {
51                check_yul_block(ctx, &case.body);
52            }
53        }
54        yul::StmtKind::FunctionDef(func) => check_yul_block(ctx, &func.body),
55        yul::StmtKind::VarDecl(_, Some(init)) => check_yul_expr(ctx, init),
56        yul::StmtKind::Leave
57        | yul::StmtKind::Break
58        | yul::StmtKind::Continue
59        | yul::StmtKind::VarDecl(_, None) => {}
60    }
61}
62
63fn check_yul_expr(ctx: &LintContext, expr: &yul::Expr<'_>) {
64    let yul::ExprKind::Call(call) = &expr.kind else { return };
65
66    if matches!(call.name.name, kw::Shl | kw::Shr | kw::Sar)
67        && let [left, right] = call.arguments.as_ref()
68        && !matches!(left.kind, yul::ExprKind::Lit(_))
69        && matches!(right.kind, yul::ExprKind::Lit(_))
70    {
71        ctx.emit(&INCORRECT_SHIFT, expr.span);
72    }
73
74    for arg in call.arguments.iter() {
75        check_yul_expr(ctx, arg);
76    }
77}