forge_lint/sol/high/
incorrect_shift.rs1use super::IncorrectShift;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use alloy_primitives::U256;
7use solar::{
8 ast::{LitKind, Stmt, StmtKind, visit::Visit, yul},
9 data_structures::Never,
10 interface::kw,
11};
12use std::ops::ControlFlow;
13
14declare_forge_lint!(
15 INCORRECT_SHIFT,
16 Severity::High,
17 "incorrect-shift",
18 "the order of args in a shift operation is incorrect"
19);
20
21impl<'ast> EarlyLintPass<'ast> for IncorrectShift {
22 fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
23 if let StmtKind::Assembly(assembly) = &stmt.kind {
24 let _ = ShiftChecker { ctx }.visit_yul_block(&assembly.block);
25 }
26 }
27}
28
29struct ShiftChecker<'a, 's> {
30 ctx: &'a LintContext<'s, 'a>,
31}
32
33impl<'ast> Visit<'ast> for ShiftChecker<'_, '_> {
34 type BreakValue = Never;
35
36 fn visit_yul_expr(&mut self, expr: &'ast yul::Expr<'ast>) -> ControlFlow<Self::BreakValue> {
37 if let yul::ExprKind::Call(call) = &expr.kind
40 && matches!(call.name.name, kw::Shl | kw::Shr | kw::Sar)
41 && let [left, right] = call.arguments.as_ref()
42 && !matches!(left.kind, yul::ExprKind::Lit(_))
43 && let yul::ExprKind::Lit(lit) = &right.kind
44 && !(call.name.name == kw::Shl
45 && matches!(lit.kind, LitKind::Number(value) if value == U256::ONE))
46 {
47 self.ctx.emit(&INCORRECT_SHIFT, expr.span);
48 }
49 self.walk_yul_expr(expr)
50 }
51}