Skip to main content

forge_lint/sol/low/
require_revert_in_loop.rs

1use super::{RequireRevertInLoop, payable_loop::visit_loop_statements_and_expressions};
2use crate::{
3    linter::{LateLintPass, LintContext},
4    sol::{Severity, SolLint},
5};
6use solar::sema::{
7    Gcx, Hir,
8    builtins::Builtin,
9    hir::{Expr, ExprKind, Function, Res, StmtKind},
10};
11use std::{cell::RefCell, collections::HashSet};
12
13declare_forge_lint!(
14    REQUIRE_REVERT_IN_LOOP,
15    Severity::Low,
16    "require-revert-in-loop",
17    "`require` or `revert` inside a loop"
18);
19
20impl<'hir> LateLintPass<'hir> for RequireRevertInLoop {
21    fn check_function(
22        &mut self,
23        ctx: &LintContext,
24        gcx: Gcx<'hir>,
25        hir: &'hir Hir<'hir>,
26        func: &'hir Function<'hir>,
27    ) {
28        let emitted = RefCell::new(HashSet::new());
29
30        visit_loop_statements_and_expressions(
31            ctx,
32            gcx,
33            hir,
34            func,
35            |ctx, _, _, stmt| {
36                if let StmtKind::Revert(expr) = stmt.kind {
37                    let mut emitted = emitted.borrow_mut();
38                    emit_once(ctx, &mut emitted, expr);
39                }
40            },
41            |ctx, _, _, expr| {
42                if is_require_or_revert_call(expr) {
43                    let mut emitted = emitted.borrow_mut();
44                    emit_once(ctx, &mut emitted, expr);
45                }
46            },
47        );
48    }
49}
50
51fn emit_once(ctx: &LintContext, emitted: &mut HashSet<solar::interface::Span>, expr: &Expr<'_>) {
52    if emitted.insert(expr.span) {
53        ctx.emit(&REQUIRE_REVERT_IN_LOOP, expr.span);
54    }
55}
56
57fn is_require_or_revert_call(expr: &Expr<'_>) -> bool {
58    let ExprKind::Call(callee, _, _) = &expr.peel_parens().kind else { return false };
59    let ExprKind::Ident(reses) = &callee.peel_parens().kind else { return false };
60
61    reses.iter().any(|res| {
62        matches!(
63            res,
64            Res::Builtin(
65                Builtin::Require | Builtin::Revert | Builtin::RevertMsg | Builtin::YulRevert
66            )
67        )
68    })
69}