neo_solidity/solidity/
solidity_analyse.rs1pub fn analyse_source(source: &str) -> Result<ContractMetadata, SolidityError> {
2 let mut contracts = analyse_all_sources(source)?;
3 Ok(contracts.swap_remove(0))
4}
5
6pub fn analyse_all_sources(source: &str) -> Result<Vec<ContractMetadata>, SolidityError> {
7 fn is_builtin_library_name(name: &str) -> bool {
8 matches!(
9 name,
10 "Runtime" | "abi" | "Storage" | "Syscalls" | "Neo" | "NativeCalls"
11 )
12 }
13
14 let mut primary = Vec::new();
15 let mut fallback = Vec::new();
16
17 for contract in parse_source(source)? {
18 if matches!(
19 contract.kind,
20 ContractKind::Contract | ContractKind::AbstractContract
21 ) {
22 primary.push(contract);
23 } else {
24 fallback.push(contract);
25 }
26 }
27
28 let has_primary = !primary.is_empty();
29 let libraries: Vec<ContractIR> = if has_primary {
30 fallback
31 .iter()
32 .filter(|contract| matches!(contract.kind, ContractKind::Library))
33 .filter(|contract| !is_builtin_library_name(contract.name.as_str()))
37 .cloned()
38 .collect()
39 } else {
40 Vec::new()
41 };
42
43 for lib in &libraries {
47 let lib_metadata = convert_contract(
48 lib.clone(),
49 &[],
50 &[],
51 std::sync::Arc::new(SelectorRegistry::default()),
52 );
53 let lib_diagnostics = validate_contract(&lib_metadata);
54 let lib_errors: Vec<Diagnostic> = lib_diagnostics
55 .into_iter()
56 .filter(|d| matches!(d.severity, DiagnosticSeverity::Error))
57 .collect();
58 if !lib_errors.is_empty() {
59 let messages: Vec<String> = lib_errors.iter().map(|d| {
60 let mut msg = d.message.clone();
61 if let Some(suggestion) = &d.suggestion {
62 msg.push_str(&format!("\n suggestion: {}", suggestion));
63 }
64 msg
65 }).collect();
66 return Err(SolidityError::analysis(messages.join("\n")));
67 }
68 }
69
70 if has_primary && !libraries.is_empty() {
73 for contract in primary.iter_mut() {
74 for lib in &libraries {
75 contract.functions.extend(lib.functions.clone());
76 contract.state_variables.extend(lib.state_variables.clone());
77 contract.structs.extend(lib.structs.clone());
78 contract.enums.extend(lib.enums.clone());
79 }
80 }
81 }
82
83 let contract_map: std::collections::HashMap<String, ContractIR> = primary
85 .iter()
86 .chain(fallback.iter())
87 .map(|contract| (contract.name.clone(), contract.clone()))
88 .collect();
89
90 let mut contract_types: Vec<String> = Vec::new();
93 let mut seen_contract_types = std::collections::HashSet::new();
94 for contract in contract_map.values() {
95 let include_as_contract_type = match contract.kind {
96 ContractKind::Contract | ContractKind::AbstractContract | ContractKind::Interface => {
97 true
98 }
99 ContractKind::Library => !is_builtin_library_name(contract.name.as_str()),
100 };
101
102 if include_as_contract_type
103 && seen_contract_types.insert(contract.name.to_ascii_lowercase())
104 {
105 contract_types.push(contract.name.clone());
106 }
107 }
108
109 let mut type_method_selectors: std::collections::HashMap<
113 String,
114 std::collections::HashMap<String, Vec<[u8; 4]>>,
115 > = std::collections::HashMap::new();
116 let mut interface_types: std::collections::HashSet<String> = std::collections::HashSet::new();
117 for contract in contract_map.values() {
118 if matches!(contract.kind, ContractKind::Interface) {
119 interface_types.insert(contract.name.clone());
120 }
121
122 let selector_contract = match contract.kind {
126 ContractKind::Contract | ContractKind::AbstractContract | ContractKind::Interface => {
127 flatten_contract_inheritance(contract.clone(), &contract_map)
128 .map(|(ir, _warnings)| ir)
129 .unwrap_or_else(|_| contract.clone())
130 }
131 ContractKind::Library => contract.clone(),
132 };
133
134 let mut per_type: std::collections::HashMap<String, Vec<[u8; 4]>> =
135 std::collections::HashMap::new();
136
137 for function in &selector_contract.functions {
138 if !matches!(function.ty, FunctionTy::Function) {
139 continue;
140 }
141
142 if !matches!(
143 function.visibility,
144 VisibilityKind::External | VisibilityKind::Public
145 ) {
146 continue;
147 }
148
149 let param_signatures: Vec<String> = function
150 .parameters
151 .iter()
152 .map(|param| canonical_param_type(¶m.ty))
153 .collect();
154 let selector = compute_function_selector(&function.name, ¶m_signatures);
155 per_type
156 .entry(function.name.clone())
157 .or_default()
158 .push(selector);
159 }
160
161 type_method_selectors.insert(contract.name.clone(), per_type);
162 }
163 let selector_registry = std::sync::Arc::new(SelectorRegistry {
164 type_method_selectors,
165 interface_types,
166 });
167
168 let mut selected = if has_primary { primary } else { fallback };
169
170 if selected.is_empty() {
171 return Ok(Vec::new());
172 }
173
174 let mut metadatas = Vec::new();
175 for contract in selected.drain(..) {
176 let (mut flattened, flatten_warnings) =
177 flatten_contract_inheritance(contract, &contract_map)?;
178 apply_modifiers_and_base_constructors(&mut flattened, &contract_map)?;
179 let mut metadata = convert_contract(
180 flattened,
181 &[],
182 &contract_types,
183 selector_registry.clone(),
184 );
185 metadata.flatten_warnings = flatten_warnings;
186 metadatas.push(metadata);
187 }
188
189 Ok(metadatas)
190}