forge_lint/sol/gas/
keccak.rs1use super::AsmKeccak256;
2use crate::{
3 linter::{LateLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::{self as ast, Span},
8 interface::kw,
9 sema::{
10 Gcx,
11 hir::{self},
12 },
13};
14
15declare_forge_lint!(
16 ASM_KECCAK256,
17 Severity::Gas,
18 "asm-keccak256",
19 "use of inefficient hashing mechanism; consider using inline assembly"
20);
21
22impl<'hir> LateLintPass<'hir> for AsmKeccak256 {
23 fn check_stmt(
24 &mut self,
25 ctx: &LintContext,
26 _gcx: Gcx<'hir>,
27 hir: &'hir hir::Hir<'hir>,
28 stmt: &'hir hir::Stmt<'hir>,
29 ) {
30 let check_expr_and_emit_lint =
31 |expr: &'hir hir::Expr<'hir>, assign: Option<ast::Ident>, is_return: bool| {
32 if let Some(hash_arg) = extract_keccak256_arg(expr) {
33 self.emit_lint(
34 ctx,
35 hir,
36 stmt.span,
37 expr,
38 hash_arg,
39 AsmContext { _assign: assign, _is_return: is_return },
40 );
41 }
42 };
43
44 match stmt.kind {
45 hir::StmtKind::DeclSingle(var_id) => {
46 let var = hir.variable(var_id);
47 if let Some(init) = var.initializer {
48 if !matches!(var.mutability, Some(hir::VarMut::Constant)) {
50 check_expr_and_emit_lint(init, var.name, false);
51 }
52 }
53 }
54 hir::StmtKind::Expr(expr)
56 | hir::StmtKind::Emit(expr)
57 | hir::StmtKind::Revert(expr)
58 | hir::StmtKind::DeclMulti(_, expr)
59 | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false),
60 hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true),
61 _ => (),
62 }
63 }
64}
65
66impl AsmKeccak256 {
67 fn emit_lint(
69 &self,
70 ctx: &LintContext,
71 _hir: &hir::Hir<'_>,
72 _stmt_span: Span,
73 call: &hir::Expr<'_>,
74 _hash: &hir::Expr<'_>,
75 _asm_ctx: AsmContext,
76 ) {
77 ctx.emit(&ASM_KECCAK256, call.span);
78 }
79}
80
81fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> {
83 let hir::ExprKind::Call(
84 callee,
85 hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. },
86 ..,
87 ) = &expr.kind
88 else {
89 return None;
90 };
91
92 let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind {
93 matches!(builtin.name(), kw::Keccak256)
94 } else {
95 return None;
96 };
97
98 (is_keccak && args.len() == 1).then(|| &args[0])
99}
100
101#[derive(Debug, Clone, Copy)]
104struct AsmContext {
105 _assign: Option<ast::Ident>,
106 _is_return: bool,
107}