Skip to main content

forge_lint/sol/med/
tx_origin.rs

1use super::TxOrigin;
2use crate::{
3    linter::{EarlyLintPass, LintContext},
4    sol::{Severity, SolLint},
5};
6use solar::{
7    ast::{Expr, ExprKind, Stmt, StmtKind, visit::Visit},
8    interface::{kw, sym},
9};
10use std::ops::ControlFlow;
11
12declare_forge_lint!(TX_ORIGIN, Severity::Med, "tx-origin", "`tx.origin` is used for authorization");
13
14impl<'ast> EarlyLintPass<'ast> for TxOrigin {
15    fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
16        if let StmtKind::If(cond, ..)
17        | StmtKind::DoWhile(_, cond)
18        | StmtKind::While(cond, _)
19        | StmtKind::For { cond: Some(cond), .. } = &stmt.kind
20        {
21            emit_if_contains_tx_origin(ctx, cond);
22        }
23    }
24
25    fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
26        if let ExprKind::Call(callee, args) = &expr.kind
27            && matches!(&callee.kind, ExprKind::Ident(id) if matches!(id.name, sym::require | sym::assert))
28            && let Some(cond) = args.exprs().next()
29        {
30            emit_if_contains_tx_origin(ctx, cond);
31        }
32    }
33}
34
35fn emit_if_contains_tx_origin<'ast>(ctx: &LintContext, expr: &'ast Expr<'ast>) {
36    if TxOriginFinder.visit_expr(expr).is_break() {
37        ctx.emit(&TX_ORIGIN, expr.span);
38    }
39}
40
41struct TxOriginFinder;
42
43impl<'ast> Visit<'ast> for TxOriginFinder {
44    type BreakValue = ();
45
46    fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow<()> {
47        if let ExprKind::Member(base, member) = &expr.kind
48            && member.name == kw::Origin
49            && matches!(&base.kind, ExprKind::Ident(id) if id.name == sym::tx)
50        {
51            return ControlFlow::Break(());
52        }
53        self.walk_expr(expr)
54    }
55}