forge_lint/sol/low/
msg_value_loop.rs1use super::{
2 MsgValueLoop,
3 payable_loop::{is_builtin, visit_payable_loop_expressions},
4};
5use crate::{
6 linter::{LateLintPass, LintContext},
7 sol::{Severity, SolLint},
8};
9use solar::{
10 interface::sym,
11 sema::{
12 Gcx,
13 hir::{Expr, ExprKind, Function, Hir},
14 },
15};
16use std::collections::HashSet;
17
18declare_forge_lint!(
19 MSG_VALUE_LOOP,
20 Severity::Low,
21 "msg-value-loop",
22 "payable functions should not use `msg.value` inside a loop"
23);
24
25impl<'hir> LateLintPass<'hir> for MsgValueLoop {
26 fn check_function(
27 &mut self,
28 ctx: &LintContext,
29 gcx: Gcx<'hir>,
30 hir: &'hir Hir<'hir>,
31 func: &'hir Function<'hir>,
32 ) {
33 let mut emitted = HashSet::new();
34 visit_payable_loop_expressions(ctx, gcx, hir, func, |ctx, _, _, expr| {
35 if is_msg_value(expr) && emitted.insert(expr.span) {
36 ctx.emit(&MSG_VALUE_LOOP, expr.span);
37 }
38 });
39 }
40}
41
42fn is_msg_value(expr: &Expr<'_>) -> bool {
43 let ExprKind::Member(base, member) = &expr.peel_parens().kind else {
44 return false;
45 };
46 member.name == sym::value && is_builtin(base, sym::msg)
47}