neo_solidity/ir/build/module/
validation.rs1fn 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}