forge_lint/sol/info/
function_init_state.rs1use super::FunctionInitState;
2use crate::{
3 linter::{LateLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 ast::StateMutability,
8 sema::{
9 Gcx,
10 hir::{ContractId, Expr, FunctionId, Hir, VariableId, Visit},
11 },
12};
13use std::{convert::Infallible, ops::ControlFlow};
14
15declare_forge_lint!(
16 FUNCTION_INIT_STATE,
17 Severity::Info,
18 "function-init-state",
19 "state variable initializer depends on a non-pure function or another state variable"
20);
21
22impl<'gcx> LateLintPass<'gcx> for FunctionInitState {
23 fn check_nested_contract(&mut self, ctx: &LintContext, gcx: Gcx<'gcx>, id: ContractId) {
24 let contract = gcx.hir.contract(id);
29 for item_id in contract.items {
30 let Some(variable) = item_id.as_variable().map(|v| gcx.hir.variable(v)) else {
31 continue;
32 };
33 if variable.is_state_variable()
34 && !variable.is_constant()
35 && let Some(initializer) = variable.initializer
36 {
37 let mut finder = ImpureRefFinder { gcx, found: false };
38 let _ = finder.visit_expr(initializer);
39 if finder.found {
40 ctx.emit(&FUNCTION_INIT_STATE, variable.span);
41 }
42 }
43 }
44 }
45}
46
47struct ImpureRefFinder<'gcx> {
50 gcx: Gcx<'gcx>,
51 found: bool,
52}
53
54impl<'gcx> Visit<'gcx> for ImpureRefFinder<'gcx> {
55 type BreakValue = Infallible;
56
57 fn hir(&self) -> &'gcx Hir<'gcx> {
58 &self.gcx.hir
59 }
60
61 fn visit_expr(&mut self, expr: &'gcx Expr<'gcx>) -> ControlFlow<Self::BreakValue> {
62 if let Some(variable_id) = self.gcx.resolved_variable(expr) {
63 self.judge_variable(variable_id);
64 } else if let Some(function_id) = self.gcx.resolved_function(expr) {
65 self.judge_function(function_id);
66 }
67 self.walk_expr(expr)
68 }
69}
70
71impl ImpureRefFinder<'_> {
72 fn judge_variable(&mut self, variable_id: VariableId) {
74 let variable = self.gcx.hir.variable(variable_id);
75 self.found |= variable.is_state_variable() && !variable.is_constant();
76 }
77
78 fn judge_function(&mut self, function_id: FunctionId) {
82 let function = self.gcx.hir.function(function_id);
83 match function.gettee {
84 Some(variable_id) => self.judge_variable(variable_id),
85 None => self.found |= function.state_mutability != StateMutability::Pure,
86 }
87 }
88}