Skip to main content

forge_lint/sol/analysis/
types.rs

1//! Type probes using Solar's type-checker results.
2
3use solar::sema::{
4    Gcx, Ty,
5    hir::{self, ContractId, Expr, TypeKind, VariableId},
6    ty::TyKind,
7};
8
9/// True if `vid` is typed as `address`/`address payable`.
10pub fn is_address_type(hir: &hir::Hir<'_>, vid: VariableId) -> bool {
11    matches!(hir.variable(vid).ty.kind, TypeKind::Elementary(hir::ElementaryType::Address(_)))
12}
13
14/// True if `id`'s elementary type matches the given ABI string.
15pub fn is_elementary(hir: &hir::Hir<'_>, id: VariableId, abi: &str) -> bool {
16    matches!(&hir.variable(id).ty.kind, TypeKind::Elementary(ty) if ty.to_abi_str() == abi)
17}
18
19/// `address` / `address payable` after peeling references.
20pub fn ty_is_address(ty: Ty<'_>) -> bool {
21    ty.peel_refs().is_address()
22}
23
24/// The contract a type denotes, through references and `type(C)`.
25pub fn ty_contract_id(ty: Ty<'_>) -> Option<ContractId> {
26    match ty.peel_refs().kind {
27        TyKind::Contract(id) => Some(id),
28        TyKind::Type(ty) => ty_contract_id(ty),
29        _ => None,
30    }
31}
32
33/// True when `expr`'s type-checked static type is `address` / `address payable`.
34pub fn expr_is_address<'gcx>(gcx: Gcx<'gcx>, expr: &Expr<'gcx>) -> bool {
35    gcx.type_of_expr(expr.peel_parens().id).is_some_and(ty_is_address)
36}
37
38/// Static contract type of a method-call receiver or direct contract/library reference.
39pub fn receiver_contract_id<'gcx>(gcx: Gcx<'gcx>, recv: &Expr<'gcx>) -> Option<ContractId> {
40    gcx.type_of_expr(recv.peel_parens().id).and_then(ty_contract_id)
41}
42
43/// The only element of `iter`, or `None` when it has zero or several.
44pub fn unique<T>(mut iter: impl Iterator<Item = T>) -> Option<T> {
45    let first = iter.next()?;
46    iter.next().is_none().then_some(first)
47}