Skip to main content

forge_lint/sol/
calls.rs

1use solar::{
2    ast::{Expr, ExprKind},
3    interface::kw,
4    sema::hir,
5};
6
7/// Checks if an expression is a low-level call.
8///
9/// Detects patterns like:
10/// - `target.call(...)`
11/// - `target.delegatecall(...)`
12/// - `target.staticcall(...)`
13/// - `target.call{value: x}(...)`
14pub(crate) const fn is_low_level_call(expr: &Expr<'_>) -> bool {
15    if let ExprKind::Call(call_expr, _args) = &expr.kind {
16        let callee = match &call_expr.kind {
17            ExprKind::CallOptions(inner_expr, _) => inner_expr,
18            _ => call_expr,
19        };
20
21        if let ExprKind::Member(_, member) = &callee.kind {
22            return matches!(member.name, kw::Call | kw::Delegatecall | kw::Staticcall);
23        }
24    }
25    false
26}
27
28/// Checks if a HIR expression is any call with an explicit gas option.
29pub(crate) fn is_call_with_gas_limit(expr: &hir::Expr<'_>) -> bool {
30    let Some((_, opts)) = call_with_options(expr) else {
31        return false;
32    };
33
34    opts.iter().any(|opt| opt.name.name == kw::Gas)
35}
36
37fn call_with_options<'hir>(
38    expr: &'hir hir::Expr<'hir>,
39) -> Option<(&'hir hir::Expr<'hir>, &'hir [hir::NamedArg<'hir>])> {
40    let hir::ExprKind::Call(callee, _, Some(opts)) = &expr.peel_parens().kind else {
41        return None;
42    };
43    Some((callee, opts))
44}