neo_solidity/solidity/analyse/inheritance/
flatten.rs1fn 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 let mut type_aliases: std::collections::HashMap<String, String> =
24 std::collections::HashMap::new();
25
26 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 state_variables.extend(ancestor.state_variables.clone());
39
40 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.extend(ancestor.events.clone());
67
68 for func in &ancestor.functions {
69 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 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 let base_is_interface = contract_map
88 .get(&base_origin)
89 .map(|c| matches!(c.kind, ContractKind::Interface))
90 .unwrap_or(false);
91
92 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 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 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 events.extend(collect_interface_events(&contract, contract_map));
148
149 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}