From 592144e30832d3f5ff9fb64c6b71d3a2712e3ac0 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 20 May 2022 23:00:36 +0300 Subject: [PATCH] fix(solc): flatten random statement order (#1292) * fix random statement order * linter --- ethers-solc/src/config.rs | 63 +++++++++++++++++----------------- ethers-solc/tests/project.rs | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs index 90307818..0311c286 100644 --- a/ethers-solc/src/config.rs +++ b/ethers-solc/src/config.rs @@ -311,41 +311,40 @@ impl ProjectPathsConfig { let mut content = content.as_bytes().to_vec(); let mut offset = 0_isize; - if strip_license { - if let Some(license) = target_node.license() { - let license_range = license.loc_by_offset(offset); - offset -= license_range.len() as isize; - content.splice(license_range, std::iter::empty()); + let mut statements = [ + (target_node.license(), strip_license), + (target_node.version(), strip_version_pragma), + (target_node.experimental(), strip_experimental_pragma), + ] + .iter() + .filter_map(|(data, condition)| if *condition { data.to_owned().as_ref() } else { None }) + .collect::>(); + statements.sort_by_key(|x| x.loc().start); + + let (mut imports, mut statements) = + (imports.iter().peekable(), statements.iter().peekable()); + while imports.peek().is_some() || statements.peek().is_some() { + let (next_import_start, next_statement_start) = ( + imports.peek().map_or(usize::max_value(), |x| x.loc().start), + statements.peek().map_or(usize::max_value(), |x| x.loc().start), + ); + if next_statement_start < next_import_start { + let repl_range = statements.next().unwrap().loc_by_offset(offset); + offset -= repl_range.len() as isize; + content.splice(repl_range, std::iter::empty()); + } else { + let import = imports.next().unwrap(); + let import_path = self.resolve_import(target_dir, import.data().path())?; + let s = self.flatten_node(&import_path, graph, imported, true, true, true)?; + + let import_content = s.as_bytes(); + let import_content_len = import_content.len() as isize; + let import_range = import.loc_by_offset(offset); + offset += import_content_len - (import_range.len() as isize); + content.splice(import_range, import_content.iter().copied()); } } - if strip_version_pragma { - if let Some(version) = target_node.version() { - let version_range = version.loc_by_offset(offset); - offset -= version_range.len() as isize; - content.splice(version_range, std::iter::empty()); - } - } - - if strip_experimental_pragma { - if let Some(experiment) = target_node.experimental() { - let experimental_pragma_range = experiment.loc_by_offset(offset); - offset -= experimental_pragma_range.len() as isize; - content.splice(experimental_pragma_range, std::iter::empty()); - } - } - - for import in imports.iter() { - let import_path = self.resolve_import(target_dir, import.data().path())?; - let s = self.flatten_node(&import_path, graph, imported, true, true, true)?; - - let import_content = s.as_bytes(); - let import_content_len = import_content.len() as isize; - let import_range = import.loc_by_offset(offset); - offset += import_content_len - (import_range.len() as isize); - content.splice(import_range, import_content.iter().copied()); - } - let result = String::from_utf8(content).map_err(|err| { SolcError::msg(format!("failed to convert extended bytes to string: {}", err)) })?; diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index 55529d51..be809dea 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -873,6 +873,71 @@ contract Contract is ParentContract, ); } +#[test] +fn can_flatten_with_version_pragma_after_imports() { + let project = TempProject::dapptools().unwrap(); + + let f = project + .add_source( + "A", + r#" +pragma solidity ^0.8.10; + +import * as B from "./B.sol"; + +contract A { } +"#, + ) + .unwrap(); + + project + .add_source( + "B", + r#" +import D from "./D.sol"; +pragma solidity ^0.8.10; +import * as C from "./C.sol"; +contract B { } +"#, + ) + .unwrap(); + + project + .add_source( + "C", + r#" +pragma solidity ^0.8.10; +contract C { } +"#, + ) + .unwrap(); + + project + .add_source( + "D", + r#" +pragma solidity ^0.8.10; +contract D { } +"#, + ) + .unwrap(); + + let result = project.flatten(&f).unwrap(); + assert_eq!( + result, + r#"pragma solidity ^0.8.10; + +contract D { } + +contract C { } + +contract B { } + +contract A { } +"# + ); +} + #[test] fn can_detect_type_error() { let project = TempProject::::dapptools().unwrap();