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