neo_solidity/frontend/
frontend_convert.rs

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    // Extract contract-level documentation
19    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                // Collect the library name for diagnostics and dispatch tracking.
48                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                // Detect advanced `using` forms so we can emit targeted diagnostics.
60                if using.ty.is_none() {
61                    // `using X for *` — ty is None when the target is `*`
62                    has_using_for_star = true;
63                }
64                if matches!(using.list, UsingList::Functions(_)) {
65                    // `using { f, g } for Y`
66                    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}