From 77931d4c63796d0ba538eb8761a3353996ce19e9 Mon Sep 17 00:00:00 2001 From: pyk Date: Thu, 7 Apr 2022 17:12:25 +0700 Subject: [PATCH] feat(solc): Strip experimental pragma from all imported contracts (#1116) * Parse experimental pragma * add integration test for experimental pragma * Fix formatting --- ethers-solc/src/config.rs | 13 ++++++-- ethers-solc/src/resolver/mod.rs | 4 +++ ethers-solc/src/resolver/parse.rs | 11 +++++-- ethers-solc/tests/project.rs | 55 +++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs index 6ffda54c..d39db454 100644 --- a/ethers-solc/src/config.rs +++ b/ethers-solc/src/config.rs @@ -250,7 +250,7 @@ impl ProjectPathsConfig { pub fn flatten(&self, target: &Path) -> Result { tracing::trace!("flattening file"); let graph = Graph::resolve(self)?; - self.flatten_node(target, &graph, &mut Default::default(), false, false) + self.flatten_node(target, &graph, &mut Default::default(), false, false, false) } /// Flattens a single node from the dependency graph @@ -260,6 +260,7 @@ impl ProjectPathsConfig { graph: &Graph, imported: &mut HashSet, strip_version_pragma: bool, + strip_experimental_pragma: bool, strip_license: bool, ) -> Result { let target_dir = target.parent().ok_or_else(|| { @@ -299,9 +300,17 @@ impl ProjectPathsConfig { } } + if strip_experimental_pragma { + if let Some(experiment) = target_node.experimental() { + let (start, end) = experiment.loc_by_offset(offset); + content.splice(start..end, std::iter::empty()); + offset -= (end - start) as isize; + } + } + for import in imports.iter() { let import_path = self.resolve_import(target_dir, import.data())?; - let s = self.flatten_node(&import_path, graph, imported, true, true)?; + let s = self.flatten_node(&import_path, graph, imported, true, true, true)?; let import_content = s.trim().as_bytes(); let import_content_len = import_content.len() as isize; let (start, end) = import.loc_by_offset(offset); diff --git a/ethers-solc/src/resolver/mod.rs b/ethers-solc/src/resolver/mod.rs index bb947ae2..a739db59 100644 --- a/ethers-solc/src/resolver/mod.rs +++ b/ethers-solc/src/resolver/mod.rs @@ -763,6 +763,10 @@ impl Node { &self.data.version } + pub fn experimental(&self) -> &Option> { + &self.data.experimental + } + pub fn license(&self) -> &Option> { &self.data.license } diff --git a/ethers-solc/src/resolver/parse.rs b/ethers-solc/src/resolver/parse.rs index eba8b739..028856f9 100644 --- a/ethers-solc/src/resolver/parse.rs +++ b/ethers-solc/src/resolver/parse.rs @@ -13,6 +13,7 @@ use std::path::{Path, PathBuf}; pub struct SolData { pub license: Option>, pub version: Option>, + pub experimental: Option>, pub imports: Vec>, pub version_req: Option, pub libraries: Vec, @@ -37,6 +38,7 @@ impl SolData { /// parsing fails, we'll fall back to extract that info via regex pub fn parse(content: &str, file: &Path) -> Self { let mut version = None; + let mut experimental = None; let mut imports = Vec::>::new(); let mut libraries = Vec::new(); let mut contracts = Vec::new(); @@ -48,7 +50,12 @@ impl SolData { SourceUnitPart::PragmaDirective(loc, _, pragma, value) => { if pragma.name == "solidity" { // we're only interested in the solidity version pragma - version = Some(SolDataUnit::new(value.string, loc.into())); + version = Some(SolDataUnit::new(value.string.clone(), loc.into())); + } + + if pragma.name == "experimental" { + experimental = + Some(SolDataUnit::new(value.string.clone(), loc.into())); } } SourceUnitPart::ImportDirective(_, import) => { @@ -106,7 +113,7 @@ impl SolData { }); let version_req = version.as_ref().and_then(|v| Solc::version_req(v.data()).ok()); - Self { version_req, version, imports, license, libraries, contracts } + Self { version_req, version, experimental, imports, license, libraries, contracts } } /// Returns `true` if the solidity file associated with this type contains a solidity library diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index 463758c3..fd3498d7 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -499,6 +499,61 @@ contract A { } ); } +#[test] +fn can_flatten_experimental_pragma() { + let project = TempProject::dapptools().unwrap(); + + let f = project + .add_source( + "A", + r#" +pragma solidity ^0.8.10; +pragma experimental ABIEncoderV2; +import "./C.sol"; +import "./B.sol"; +contract A { } +"#, + ) + .unwrap(); + + project + .add_source( + "B", + r#" +pragma solidity ^0.8.10; +pragma experimental ABIEncoderV2; +import "./C.sol"; +contract B { } +"#, + ) + .unwrap(); + + project + .add_source( + "C", + r#" +pragma solidity ^0.8.10; +pragma experimental ABIEncoderV2; +import "./A.sol"; +contract C { } +"#, + ) + .unwrap(); + + let result = project.flatten(&f).unwrap(); + + assert_eq!( + result, + r#" +pragma solidity ^0.8.10; +pragma experimental ABIEncoderV2; +contract C { } +contract B { } +contract A { } +"# + ); +} + #[test] fn can_flatten_file_with_duplicates() { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/test-flatten-duplicates");