neo_solidity/solidity/analyse/inheritance/
flatten.rs

1fn flatten_contract_inheritance(
2    contract: ContractIR,
3    contract_map: &std::collections::HashMap<String, ContractIR>,
4) -> Result<(ContractIR, Vec<String>), SolidityError> {
5    let order = contract_linearization_base_to_derived(&contract.name, contract_map)?;
6
7    let mut functions: Vec<FunctionIR> = Vec::new();
8    let mut function_index: std::collections::HashMap<(u8, String, usize), (String, usize)> =
9        std::collections::HashMap::new();
10
11    let mut events: Vec<EventIR> = Vec::new();
12    let mut state_variables: Vec<StateVariableIR> = Vec::new();
13
14    let mut structs: Vec<StructIR> = Vec::new();
15    let mut struct_index: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
16
17    let mut enums: Vec<EnumIR> = Vec::new();
18    let mut enum_index: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
19
20    let mut warnings: Vec<String> = Vec::new();
21
22    // Merge user-defined value type aliases from the inheritance chain.
23    let mut type_aliases: std::collections::HashMap<String, String> =
24        std::collections::HashMap::new();
25
26    // Maps original method name → renamed super-method name for `super.method()` resolution.
27    let mut super_method_map: std::collections::HashMap<String, String> =
28        std::collections::HashMap::new();
29
30    for ancestor_name in &order {
31        let Some(ancestor) = contract_map.get(ancestor_name) else {
32            continue;
33        };
34
35        let ancestor_is_interface = matches!(ancestor.kind, ContractKind::Interface);
36
37        // Preserve Solidity storage layout order: base state variables first, then derived.
38        state_variables.extend(ancestor.state_variables.clone());
39
40        // Merge user-defined value type aliases (base-first, derived wins on conflict).
41        for (alias_name, underlying) in &ancestor.type_aliases {
42            type_aliases
43                .entry(alias_name.clone())
44                .or_insert_with(|| underlying.clone());
45        }
46
47        for s in &ancestor.structs {
48            if let Some(idx) = struct_index.get(&s.name).copied() {
49                structs[idx] = s.clone();
50            } else {
51                struct_index.insert(s.name.clone(), structs.len());
52                structs.push(s.clone());
53            }
54        }
55
56        for e in &ancestor.enums {
57            if let Some(idx) = enum_index.get(&e.name).copied() {
58                enums[idx] = e.clone();
59            } else {
60                enum_index.insert(e.name.clone(), enums.len());
61                enums.push(e.clone());
62            }
63        }
64
65        // Events are additive in Solidity; duplicates are later de-duplicated by manifest builder.
66        events.extend(ancestor.events.clone());
67
68        for func in &ancestor.functions {
69            // When flattening, keep only the most-derived constructor to avoid name collisions.
70            if matches!(func.ty, FunctionTy::Constructor) && ancestor.name != contract.name {
71                continue;
72            }
73
74            let key = (function_ty_key(func.ty), func.name.clone(), func.parameters.len());
75            match function_index.get(&key) {
76                Some((origin, _)) if origin == &ancestor.name => {
77                    // Duplicate definition within the same contract; preserve it so validation can
78                    // emit a proper DUPLICATE_SIGNATURE diagnostic.
79                    functions.push(func.clone());
80                }
81                Some((base_origin, idx)) => {
82                    let idx = *idx;
83                    let base_func = &functions[idx];
84                    let base_origin = base_origin.clone();
85
86                    // Determine if the base contract is an interface.
87                    let base_is_interface = contract_map
88                        .get(&base_origin)
89                        .map(|c| matches!(c.kind, ContractKind::Interface))
90                        .unwrap_or(false);
91
92                    // Virtual/override enforcement:
93                    // - Base function must be `virtual` (or from an interface, which is implicitly virtual)
94                    // - Derived function must be marked `override`
95                    if !base_is_interface && !base_func.is_virtual {
96                        warnings.push(format!(
97                            "function '{}' in '{}' overrides '{}::{}' which is not marked 'virtual'",
98                            func.name, ancestor_name, base_origin, func.name
99                        ));
100                    }
101
102                    if !ancestor_is_interface && !func.is_override {
103                        warnings.push(format!(
104                            "function '{}' in '{}' overrides a base function but is not marked 'override'",
105                            func.name, ancestor_name
106                        ));
107                    }
108
109                    // Preserve the base method as a renamed internal function so that
110                    // `super.method()` can resolve to it during IR lowering.
111                    // Only preserve if the base function has a body (skip interface stubs).
112                    // In multi-level inheritance (A→B→C), only keep the most recent
113                    // base version — replace any existing `__super_*` entry.
114                    if base_func.body.is_some() {
115                        let super_name = format!("__super_{}", func.name);
116                        let mut super_func = base_func.clone();
117                        super_func.name = super_name.clone();
118                        super_func.visibility = VisibilityKind::Internal;
119                        super_func.is_virtual = false;
120                        super_func.is_override = false;
121
122                        // Replace existing __super_ entry if present, otherwise append.
123                        if let Some(existing_idx) = functions
124                            .iter()
125                            .position(|f| f.name == super_name)
126                        {
127                            functions[existing_idx] = super_func;
128                        } else {
129                            functions.push(super_func);
130                        }
131                        super_method_map.insert(func.name.clone(), super_name);
132                    }
133
134                    functions[idx] = func.clone();
135                    function_index.insert(key, (ancestor.name.clone(), idx));
136                }
137                None => {
138                    let idx = functions.len();
139                    functions.push(func.clone());
140                    function_index.insert(key, (ancestor.name.clone(), idx));
141                }
142            }
143        }
144    }
145
146    // Merge interface events from the full inheritance tree.
147    events.extend(collect_interface_events(&contract, contract_map));
148
149    // Contracts may reference structs/enums declared on inherited interfaces, but interface types
150    // are excluded from the storage linearization order. Merge them explicitly so type parsing
151    // (NeoType inference) and IR lowering can recognize `Interface.StructName` and unqualified
152    // `StructName` references used in contract bodies and ABI signatures.
153    if matches!(
154        contract.kind,
155        ContractKind::Contract | ContractKind::AbstractContract
156    ) {
157        let (iface_structs, iface_enums) = collect_interface_types(&contract, contract_map);
158        for s in iface_structs {
159            if struct_index.contains_key(&s.name) {
160                continue;
161            }
162            struct_index.insert(s.name.clone(), structs.len());
163            structs.push(s);
164        }
165        for e in iface_enums {
166            if enum_index.contains_key(&e.name) {
167                continue;
168            }
169            enum_index.insert(e.name.clone(), enums.len());
170            enums.push(e);
171        }
172    }
173
174    Ok((ContractIR {
175        name: contract.name,
176        kind: contract.kind,
177        bases: contract.bases,
178        functions,
179        events,
180        state_variables,
181        structs,
182        enums,
183        doc: contract.doc,
184        has_using_for_star: contract.has_using_for_star,
185        has_using_function_list: contract.has_using_function_list,
186        using_for_libraries: contract.using_for_libraries,
187        has_type_definitions: contract.has_type_definitions,
188        type_aliases,
189        super_method_map,
190    }, warnings))
191}
192
193fn inheritance_contract_chain(
194    contract: &ContractIR,
195    contract_map: &std::collections::HashMap<String, ContractIR>,
196) -> Result<Vec<String>, SolidityError> {
197    contract_linearization_base_to_derived(&contract.name, contract_map)
198}