1use crate::frontend::VisibilityKind;
4use crate::solidity::{
5 ContractMetadata, Diagnostic, DiagnosticSeverity, FunctionKind, FunctionMetadata,
6 ParameterMetadata, StateMutability, StateVariableMetadata,
7};
8use crate::type_system::NeoType;
9
10#[derive(Debug, Clone)]
11pub struct SemanticModel {
12 pub functions: Vec<FunctionSymbol>,
13 pub state_variables: Vec<StateVariableSymbol>,
14}
15
16impl SemanticModel {
17 pub fn public_functions(&self) -> Vec<&FunctionSymbol> {
19 self.functions
20 .iter()
21 .filter(|f| {
22 matches!(
23 f.visibility,
24 VisibilityKind::Public | VisibilityKind::External
25 )
26 })
27 .collect()
28 }
29
30 pub fn get_function(&self, name: &str) -> Option<&FunctionSymbol> {
32 self.functions.iter().find(|f| f.name == name)
33 }
34
35 pub fn get_state_variables(&self) -> &[StateVariableSymbol] {
37 &self.state_variables
38 }
39
40 pub fn is_payable(&self) -> bool {
42 self.functions
43 .iter()
44 .any(|f| f.state_mutability == StateMutability::Payable)
45 }
46}
47
48#[derive(Debug, Clone)]
49pub struct FunctionSymbol {
50 pub name: String,
51 pub kind: FunctionKind,
52 pub parameters: Vec<ParameterSymbol>,
53 pub returns: Vec<ParameterSymbol>,
54 pub state_mutability: StateMutability,
55 pub visibility: VisibilityKind,
56}
57
58#[derive(Debug, Clone)]
59pub struct ParameterSymbol {
60 pub name: Option<String>,
61 pub ty: NeoType,
62 pub storage: Option<String>,
63}
64
65#[derive(Debug, Clone)]
66pub struct StateVariableSymbol {
67 pub name: Option<String>,
68 pub ty: NeoType,
69 pub is_constant: bool,
70 pub is_immutable: bool,
71 pub visibility: Option<String>,
72}
73
74pub fn build_semantic_model(metadata: &ContractMetadata) -> Result<SemanticModel, Vec<Diagnostic>> {
75 let mut diagnostics = Vec::new();
76
77 let mut functions = Vec::new();
78 for function in &metadata.methods {
79 match convert_function(function) {
80 Ok(symbol) => functions.push(symbol),
81 Err(mut diags) => diagnostics.append(&mut diags),
82 }
83 }
84
85 let mut state_variables = Vec::new();
86 for state in &metadata.state_variables {
87 match convert_state_variable(state) {
88 Ok(symbol) => state_variables.push(symbol),
89 Err(mut diags) => diagnostics.append(&mut diags),
90 }
91 }
92
93 let has_error = diagnostics
94 .iter()
95 .any(|diag| matches!(diag.severity, DiagnosticSeverity::Error));
96
97 if has_error {
98 Err(diagnostics)
99 } else {
100 Ok(SemanticModel {
101 functions,
102 state_variables,
103 })
104 }
105}
106
107fn convert_function(function: &FunctionMetadata) -> Result<FunctionSymbol, Vec<Diagnostic>> {
108 let mut diagnostics = Vec::new();
109 let allow_unsupported_internal_types = !matches!(
110 function.visibility,
111 VisibilityKind::Public | VisibilityKind::External
112 );
113
114 let mut parameters = Vec::new();
115 for param in &function.parameters {
116 match convert_parameter(
117 param,
118 FunctionSide::Parameter,
119 &function.name,
120 allow_unsupported_internal_types,
121 ) {
122 Ok(symbol) => parameters.push(symbol),
123 Err(diag) => diagnostics.push(diag),
124 }
125 }
126
127 let mut returns = Vec::new();
128 for param in &function.return_parameters {
129 match convert_parameter(
130 param,
131 FunctionSide::Return,
132 &function.name,
133 allow_unsupported_internal_types,
134 ) {
135 Ok(symbol) => returns.push(symbol),
136 Err(diag) => diagnostics.push(diag),
137 }
138 }
139
140 if diagnostics
141 .iter()
142 .any(|diag| matches!(diag.severity, DiagnosticSeverity::Error))
143 {
144 Err(diagnostics)
145 } else {
146 Ok(FunctionSymbol {
147 name: function.name.clone(),
148 kind: function.kind,
149 parameters,
150 returns,
151 state_mutability: function.state_mutability,
152 visibility: function.visibility,
153 })
154 }
155}
156
157fn convert_state_variable(
158 state: &StateVariableMetadata,
159) -> Result<StateVariableSymbol, Vec<Diagnostic>> {
160 match &state.neo_type {
161 Some(neo_type) => Ok(StateVariableSymbol {
162 name: state.name.clone(),
163 ty: neo_type.clone(),
164 is_constant: state.is_constant,
165 is_immutable: state.is_immutable,
166 visibility: state.visibility.clone(),
167 }),
168 None => Err(vec![Diagnostic::error(format!(
169 "state variable '{}' has unsupported type '{}'",
170 state.name.as_deref().unwrap_or("<unnamed>"),
171 state.ty
172 ))]),
173 }
174}
175
176enum FunctionSide {
177 Parameter,
178 Return,
179}
180
181fn convert_parameter(
182 param: &ParameterMetadata,
183 side: FunctionSide,
184 function_name: &str,
185 allow_unsupported_internal_types: bool,
186) -> Result<ParameterSymbol, Diagnostic> {
187 match ¶m.neo_type {
188 Some(neo_type) => Ok(ParameterSymbol {
189 name: param.name.clone(),
190 ty: neo_type.clone(),
191 storage: param.storage.clone(),
192 }),
193 None if allow_unsupported_internal_types => Ok(ParameterSymbol {
194 name: param.name.clone(),
195 ty: NeoType::Any,
196 storage: param.storage.clone(),
197 }),
198 None => Err(Diagnostic::error(match side {
199 FunctionSide::Parameter => format!(
200 "function '{}' parameter '{}' uses unsupported type '{}'",
201 function_name,
202 param
203 .name
204 .clone()
205 .unwrap_or_else(|| "<unnamed>".to_string()),
206 param.ty
207 ),
208 FunctionSide::Return => format!(
209 "function '{}' return type '{}' is unsupported",
210 function_name, param.ty
211 ),
212 })),
213 }
214}