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