forge_lint/sol/analysis/
primitives.rs1use solar::{
4 ast::LitKind,
5 interface::{Symbol, kw, sym},
6 sema::hir::{self, ElementaryType, Expr, ExprKind, Res, Stmt, StmtKind, TypeKind, VariableId},
7};
8
9pub fn is_address_type(hir: &hir::Hir<'_>, vid: VariableId) -> bool {
11 matches!(hir.variable(vid).ty.kind, TypeKind::Elementary(ElementaryType::Address(_)))
12}
13
14pub fn is_require_or_assert(callee: &Expr<'_>) -> bool {
16 matches!(&callee.kind, ExprKind::Ident(reses)
17 if reses.iter().any(|r| matches!(r,
18 Res::Builtin(b) if b.name() == sym::require || b.name() == sym::assert)))
19}
20
21pub fn address_call_receiver<'a>(callee: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
24 let inner = match &callee.kind {
26 ExprKind::Call(inner, ..) => inner,
27 _ => callee,
28 };
29 let target = if matches!(inner.kind, ExprKind::Member(..)) { inner } else { callee };
30 if let ExprKind::Member(receiver, name) = &target.kind {
31 let n = name.name;
32 if n == kw::Call || n == kw::Delegatecall || n == sym::transfer || n == sym::send {
33 return Some(receiver);
34 }
35 }
36 None
37}
38
39pub fn branch_always_exits(stmt: &Stmt<'_>) -> bool {
44 match &stmt.kind {
45 StmtKind::Return(_) | StmtKind::Revert(_) => true,
46 StmtKind::Expr(expr) => is_exit_call(expr),
47 StmtKind::Block(b) | StmtKind::UncheckedBlock(b) => b.stmts.iter().any(branch_always_exits),
48 StmtKind::If(_, t, Some(e)) => branch_always_exits(t) && branch_always_exits(e),
49 _ => false,
50 }
51}
52
53fn is_builtin(expr: &Expr<'_>, name: Symbol) -> bool {
55 matches!(&expr.peel_parens().kind, ExprKind::Ident(reses)
56 if reses.iter().any(|r| matches!(r, Res::Builtin(b) if b.name() == name)))
57}
58
59fn is_exit_call(expr: &Expr<'_>) -> bool {
60 let ExprKind::Call(callee, args, _) = &expr.peel_parens().kind else { return false };
61 if is_builtin(callee, kw::Revert) {
62 return true;
63 }
64 if is_require_or_assert(callee)
65 && let Some(first) = args.exprs().next()
66 && matches!(
67 &first.peel_parens().kind,
68 ExprKind::Lit(lit) if matches!(lit.kind, LitKind::Bool(false))
69 )
70 {
71 return true;
72 }
73 false
74}