forge_lint/sol/gas/
custom_errors.rs1use super::CustomErrors;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::{CallArgs, CallArgsKind, Expr, ExprKind, LitKind},
8 interface::{kw, sym},
9};
10
11declare_forge_lint!(
12 CUSTOM_ERRORS,
13 Severity::Gas,
14 "custom-errors",
15 "`revert` or `require` call does not use a custom error"
16);
17
18impl<'ast> EarlyLintPass<'ast> for CustomErrors {
19 fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
20 let ExprKind::Call(callee, CallArgs { kind: CallArgsKind::Unnamed(args), .. }) = &expr.kind
21 else {
22 return;
23 };
24 let ExprKind::Ident(ident) = &callee.kind else { return };
25 let lint = match ident.name {
27 sym::require => args.len() == 1 || args.get(1).is_some_and(|e| is_string_literal(e)),
28 kw::Revert => args.first().is_none_or(|e| is_string_literal(e)),
29 _ => false,
30 };
31 if lint {
32 ctx.emit(&CUSTOM_ERRORS, expr.span);
33 }
34 }
35}
36
37const fn is_string_literal(expr: &Expr<'_>) -> bool {
38 matches!(&expr.kind, ExprKind::Lit(lit, _) if matches!(lit.kind, LitKind::Str(..)))
39}