forge_lint/sol/info/
too_many_digits.rs1use super::TooManyDigits;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::{Expr, ExprKind, Lit, LitKind, Stmt, StmtKind, visit::Visit},
8 data_structures::Never,
9};
10use std::ops::ControlFlow;
11
12declare_forge_lint!(
13 TOO_MANY_DIGITS,
14 Severity::Info,
15 "too-many-digits",
16 "numeric literal contains a long sequence of zeros"
17);
18
19impl<'ast> EarlyLintPass<'ast> for TooManyDigits {
20 fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
21 if let StmtKind::Assembly(assembly) = &stmt.kind {
23 let _ = YulLiterals { ctx }.visit_yul_block(&assembly.block);
24 }
25 }
26
27 fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
28 if let ExprKind::Lit(lit, None) = &expr.kind {
30 check_lit(ctx, lit);
31 }
32 }
33}
34
35fn check_lit(ctx: &LintContext, lit: &Lit<'_>) {
36 if !matches!(lit.kind, LitKind::Number(_)) {
38 return;
39 }
40 let s = lit.symbol.as_str();
41 let hex = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X"));
42 let is_hex_address =
45 hex.is_some_and(|h| h.len() == 40 && h.bytes().all(|b| b.is_ascii_hexdigit()));
46 let is_scientific = hex.is_none() && s.contains(['e', 'E']);
47 if !is_hex_address && !is_scientific && s.contains("00000") {
50 ctx.emit(&TOO_MANY_DIGITS, lit.span);
51 }
52}
53
54struct YulLiterals<'a, 's> {
56 ctx: &'a LintContext<'s, 'a>,
57}
58
59impl<'ast> Visit<'ast> for YulLiterals<'_, '_> {
60 type BreakValue = Never;
61
62 fn visit_lit(&mut self, lit: &'ast Lit<'_>) -> ControlFlow<Self::BreakValue> {
63 check_lit(self.ctx, lit);
64 ControlFlow::Continue(())
65 }
66}