1fn convert_contract(
2 contract: ContractDefinition,
3 comment_map: &HashMap<usize, NatspecDocIR>,
4) -> ContractIR {
5 let name = contract
6 .name
7 .as_ref()
8 .map(|id| id.name.clone())
9 .unwrap_or_else(|| "Contract".to_string());
10
11 let kind = match contract.ty {
12 ContractTy::Abstract(_) => ContractKind::AbstractContract,
13 ContractTy::Contract(_) => ContractKind::Contract,
14 ContractTy::Interface(_) => ContractKind::Interface,
15 ContractTy::Library(_) => ContractKind::Library,
16 };
17
18 let doc = find_preceding_doc(&contract.loc, comment_map);
20
21 let bases = contract.base;
22
23 let mut functions = Vec::new();
24 let mut events = Vec::new();
25 let mut state_variables = Vec::new();
26 let mut structs = Vec::new();
27 let mut enums = Vec::new();
28 let mut has_using_for_star = false;
29 let mut has_using_function_list = false;
30 let mut using_for_libraries: Vec<String> = Vec::new();
31 let mut has_type_definitions = false;
32 let mut type_aliases: std::collections::HashMap<String, String> =
33 std::collections::HashMap::new();
34
35 for part in contract.parts.into_iter() {
36 match part {
37 ContractPart::FunctionDefinition(def) => {
38 functions.push(convert_function(*def, comment_map))
39 }
40 ContractPart::EventDefinition(def) => events.push(convert_event(*def)),
41 ContractPart::VariableDefinition(def) => {
42 state_variables.push(convert_state_variable(*def))
43 }
44 ContractPart::StructDefinition(def) => structs.push(convert_struct(*def)),
45 ContractPart::EnumDefinition(def) => enums.push(convert_enum(*def)),
46 ContractPart::Using(using) => {
47 if let UsingList::Library(ref path) = using.list {
49 let lib_name: String = path
50 .identifiers
51 .iter()
52 .map(|id| id.name.as_str())
53 .collect::<Vec<_>>()
54 .join(".");
55 if !using_for_libraries.contains(&lib_name) {
56 using_for_libraries.push(lib_name);
57 }
58 }
59 if using.ty.is_none() {
61 has_using_for_star = true;
63 }
64 if matches!(using.list, UsingList::Functions(_)) {
65 has_using_function_list = true;
67 }
68 }
69 ContractPart::TypeDefinition(td) => {
70 has_type_definitions = true;
71 let underlying = format!("{}", td.ty);
72 type_aliases.insert(td.name.name.clone(), underlying);
73 }
74 _ => {}
75 }
76 }
77
78 ContractIR {
79 name,
80 kind,
81 bases,
82 functions,
83 events,
84 state_variables,
85 structs,
86 enums,
87 doc,
88 has_using_for_star,
89 has_using_function_list,
90 using_for_libraries,
91 has_type_definitions,
92 type_aliases,
93 super_method_map: std::collections::HashMap::new(),
94 }
95}
96
97fn convert_function(
98 function: FunctionDefinition,
99 comment_map: &HashMap<usize, NatspecDocIR>,
100) -> FunctionIR {
101 let name = function_name(&function);
102 let mutability = extract_mutability(&function);
103 let visibility = extract_visibility(&function);
104 let doc = find_preceding_doc(&function.loc, comment_map);
105
106 let mut is_virtual = false;
107 let mut is_override = false;
108 let mut base_or_modifiers: Vec<Base> = Vec::new();
109
110 for attr in &function.attributes {
111 match attr {
112 FunctionAttribute::Virtual(_) => is_virtual = true,
113 FunctionAttribute::Override(_, _) => is_override = true,
114 FunctionAttribute::BaseOrModifier(_, base) => base_or_modifiers.push(base.clone()),
115 _ => {}
116 }
117 }
118
119 let parameters = convert_parameters(&function.params);
120 let returns = convert_parameters(&function.returns);
121 FunctionIR {
122 name,
123 ty: function.ty,
124 parameters,
125 returns,
126 mutability,
127 visibility,
128 is_virtual,
129 is_override,
130 base_or_modifiers,
131 body: function.body,
132 doc,
133 }
134}
135
136fn function_name(function: &FunctionDefinition) -> String {
137 match (&function.name, function.ty) {
138 (Some(Identifier { name, .. }), _) => name.clone(),
139 (None, FunctionTy::Constructor) => "constructor".to_string(),
140 (None, FunctionTy::Fallback) => "fallback".to_string(),
141 (None, FunctionTy::Receive) => "receive".to_string(),
142 (None, FunctionTy::Modifier) => "modifier".to_string(),
143 _ => "function".to_string(),
144 }
145}
146
147fn extract_mutability(function: &FunctionDefinition) -> MutabilityKind {
148 for attribute in &function.attributes {
149 if let FunctionAttribute::Mutability(m) = attribute {
150 return match m {
151 Mutability::Pure(_) => MutabilityKind::Pure,
152 Mutability::View(_) | Mutability::Constant(_) => MutabilityKind::View,
153 Mutability::Payable(_) => MutabilityKind::Payable,
154 };
155 }
156 }
157
158 MutabilityKind::NonPayable
159}
160
161fn extract_visibility(function: &FunctionDefinition) -> VisibilityKind {
162 for attribute in &function.attributes {
163 if let FunctionAttribute::Visibility(visibility) = attribute {
164 return match visibility {
165 Visibility::External(_) => VisibilityKind::External,
166 Visibility::Public(_) => VisibilityKind::Public,
167 Visibility::Internal(_) => VisibilityKind::Internal,
168 Visibility::Private(_) => VisibilityKind::Private,
169 };
170 }
171 }
172
173 VisibilityKind::Internal
174}
175
176fn convert_parameters(params: &ParameterList) -> Vec<ParameterIR> {
177 params
178 .iter()
179 .filter_map(|(_, param)| param.as_ref())
180 .map(|param| ParameterIR {
181 name: param.name.as_ref().map(|id| id.name.clone()),
182 ty: format!("{}", param.ty),
183 storage: param.storage.as_ref().map(storage_to_string),
184 })
185 .collect()
186}
187
188fn storage_to_string(storage: &StorageLocation) -> String {
189 match storage {
190 StorageLocation::Memory(_) => "memory",
191 StorageLocation::Storage(_) => "storage",
192 StorageLocation::Calldata(_) => "calldata",
193 }
194 .to_string()
195}
196
197fn convert_event(event: EventDefinition) -> EventIR {
198 let name = event
199 .name
200 .as_ref()
201 .map(|id| id.name.clone())
202 .unwrap_or_else(|| "event".to_string());
203
204 let parameters = event
205 .fields
206 .into_iter()
207 .map(|param| EventParameterIR {
208 name: param.name.map(|id| id.name),
209 ty: format!("{}", param.ty),
210 indexed: param.indexed,
211 })
212 .collect();
213
214 EventIR { name, parameters }
215}
216
217fn convert_state_variable(def: VariableDefinition) -> StateVariableIR {
218 let name = def.name.map(|id| id.name);
219 let ty = format!("{}", def.ty);
220 let initializer = def.initializer.clone();
221
222 let mut visibility = None;
223 let mut is_constant = false;
224 let mut is_immutable = false;
225
226 for attr in def.attrs {
227 match attr {
228 VariableAttribute::Visibility(vis) => {
229 visibility = Some(
230 match vis {
231 Visibility::External(_) => "external",
232 Visibility::Public(_) => "public",
233 Visibility::Internal(_) => "internal",
234 Visibility::Private(_) => "private",
235 }
236 .to_string(),
237 );
238 }
239 VariableAttribute::Constant(_) => {
240 is_constant = true;
241 }
242 VariableAttribute::Immutable(_) => {
243 is_immutable = true;
244 }
245 _ => {}
246 }
247 }
248
249 StateVariableIR {
250 name,
251 ty,
252 is_constant,
253 is_immutable,
254 visibility,
255 has_initializer: initializer.is_some(),
256 initializer,
257 }
258}
259
260fn convert_struct(def: StructDefinition) -> StructIR {
261 let name = def
262 .name
263 .as_ref()
264 .map(|id| id.name.clone())
265 .unwrap_or_else(|| "Struct".to_string());
266
267 let fields = def
268 .fields
269 .into_iter()
270 .filter_map(|field| {
271 let field_name = field.name.map(|id| id.name)?;
272 Some(StructFieldIR {
273 name: field_name,
274 ty: format!("{}", field.ty),
275 })
276 })
277 .collect();
278
279 StructIR { name, fields }
280}
281
282fn convert_enum(def: EnumDefinition) -> EnumIR {
283 let name = def
284 .name
285 .as_ref()
286 .map(|id| id.name.clone())
287 .unwrap_or_else(|| "Enum".to_string());
288
289 let values = def
290 .values
291 .into_iter()
292 .filter_map(|value| value.map(|id| id.name))
293 .collect();
294
295 EnumIR { name, values }
296}