neo_solidity/ir/build/module/
validation.rs

1fn compute_transitive_hazard_map(functions: &[Function]) -> HashMap<String, Hazards> {
2    let mut direct = HashMap::new();
3    for function in functions {
4        direct.insert(function.name.clone(), direct_hazards(function));
5    }
6
7    let graph = build_call_graph(functions);
8    let mut memo = HashMap::new();
9    let mut visiting = HashSet::new();
10
11    for name in direct.keys() {
12        compute_transitive_hazards(name, &direct, &graph, &mut memo, &mut visiting);
13    }
14
15    memo
16}
17
18fn validate_safe_methods(
19    metadata: &ContractMetadata,
20    hazards: &HashMap<String, Hazards>,
21) -> Vec<IrDiagnostic> {
22    let mut errors = Vec::new();
23
24    for method in &metadata.methods {
25        if method.state_mutability != crate::solidity::StateMutability::View {
26            continue;
27        }
28
29        let hazards = hazards
30            .get(&method.neo_name)
31            .copied()
32            .unwrap_or_default();
33
34        if !hazards.safe_violation() {
35            continue;
36        }
37
38        if hazards.writes_state {
39            errors.push(IrDiagnostic {
40                function_name: method.neo_name.clone(),
41                message: "declared view/pure but writes contract storage".to_string(),
42                suggestion: None,
43                code: None,
44            });
45        }
46
47        if hazards.notifies {
48            errors.push(IrDiagnostic {
49                function_name: method.neo_name.clone(),
50                message: "declared view/pure but emits events/notifications".to_string(),
51                suggestion: None,
52                code: None,
53            });
54        }
55
56        if hazards.unsafe_contract_call {
57            errors.push(IrDiagnostic {
58                function_name: method.neo_name.clone(),
59                message: "declared view/pure but performs non-readonly contract calls".to_string(),
60                suggestion: None,
61                code: None,
62            });
63        }
64    }
65
66    errors
67}
68
69fn validate_pure_methods(
70    metadata: &ContractMetadata,
71    hazards: &HashMap<String, Hazards>,
72) -> Vec<IrDiagnostic> {
73    let mut errors = Vec::new();
74
75    for method in &metadata.methods {
76        if method.state_mutability != crate::solidity::StateMutability::Pure {
77            continue;
78        }
79
80        let hazards = hazards
81            .get(&method.neo_name)
82            .copied()
83            .unwrap_or_default();
84
85        if !hazards.pure_violation() {
86            continue;
87        }
88
89        if hazards.writes_state {
90            errors.push(IrDiagnostic {
91                function_name: method.neo_name.clone(),
92                message: "declared pure but writes contract storage".to_string(),
93                suggestion: None,
94                code: None,
95            });
96        }
97
98        if hazards.notifies {
99            errors.push(IrDiagnostic {
100                function_name: method.neo_name.clone(),
101                message: "declared pure but emits events/notifications".to_string(),
102                suggestion: None,
103                code: None,
104            });
105        }
106
107        if hazards.reads_state {
108            errors.push(IrDiagnostic {
109                function_name: method.neo_name.clone(),
110                message: "declared pure but reads contract storage".to_string(),
111                suggestion: None,
112                code: None,
113            });
114        }
115
116        if hazards.reads_environment {
117            errors.push(IrDiagnostic {
118                function_name: method.neo_name.clone(),
119                message: "declared pure but reads the execution environment".to_string(),
120                suggestion: None,
121                code: None,
122            });
123        }
124
125        if hazards.contract_calls {
126            let label = if hazards.unsafe_contract_call {
127                "performs non-readonly contract calls"
128            } else {
129                "performs contract calls"
130            };
131            errors.push(IrDiagnostic {
132                function_name: method.neo_name.clone(),
133                message: format!("declared pure but {}", label),
134                suggestion: None,
135                code: None,
136            });
137        }
138    }
139
140    errors
141}