forge_lint/sol/low/
require_revert_in_loop.rs1use super::{
2 RequireRevertInLoop,
3 payable_loop::{LoopItem, for_each_loop_item},
4};
5use crate::{
6 linter::{LateLintPass, LintContext},
7 sol::{Severity, SolLint},
8};
9use solar::sema::{
10 Gcx,
11 builtins::Builtin,
12 hir::{Expr, ExprKind, Function, StmtKind},
13};
14
15declare_forge_lint!(
16 REQUIRE_REVERT_IN_LOOP,
17 Severity::Low,
18 "require-revert-in-loop",
19 "`require` or `revert` inside a loop"
20);
21
22impl<'gcx> LateLintPass<'gcx> for RequireRevertInLoop {
23 fn check_function(&mut self, ctx: &LintContext, gcx: Gcx<'gcx>, func: &'gcx Function<'gcx>) {
24 for_each_loop_item(gcx, func, false, |item| {
25 let reported = match item {
26 LoopItem::Stmt(stmt) => match stmt.kind {
27 StmtKind::Revert(expr) => Some(expr),
28 _ => None,
29 },
30 LoopItem::Expr(expr) => is_require_or_revert_call(gcx, expr).then_some(expr),
31 };
32 if let Some(expr) = reported {
33 ctx.emit(&REQUIRE_REVERT_IN_LOOP, expr.span);
34 }
35 });
36 }
37}
38
39fn is_require_or_revert_call(gcx: Gcx<'_>, expr: &Expr<'_>) -> bool {
41 let ExprKind::Call(callee, ..) = &expr.peel_parens().kind else { return false };
42 matches!(
43 gcx.resolved_builtin(callee),
44 Some(Builtin::Require | Builtin::Revert | Builtin::RevertMsg | Builtin::YulRevert)
45 )
46}