Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

fix(solc): flatten duplicates #813

Merged
merged 5 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

### Unreleased

- Fix duplicate files during flattening
[#813](https://github.com/gakonst/ethers-rs/pull/813)
- Add ability to flatten file imports
[#774](https://github.com/gakonst/ethers-rs/pull/774)
- Add dependency graph and resolve all imported libraryfiles
Expand Down
11 changes: 9 additions & 2 deletions ethers-solc/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,15 @@ impl ProjectPathsConfig {
pub fn flatten(&self, target: &Path) -> Result<String> {
tracing::trace!("flattening file");
let graph = Graph::resolve(self)?;
self.flatten_node(target, &graph, false, false)
self.flatten_node(target, &graph, &mut vec![], false, false)
}

/// Flattens a single node from the dependency graph
fn flatten_node(
&self,
target: &Path,
graph: &Graph,
imported: &mut Vec<usize>,
strip_version_pragma: bool,
strip_license: bool,
) -> Result<String> {
Expand All @@ -193,6 +194,11 @@ impl ProjectPathsConfig {
let target_index = graph.files().get(target).ok_or_else(|| {
SolcError::msg(format!("cannot resolve file at \"{:?}\"", target.display()))
})?;

if imported.iter().any(|&idx| idx == *target_index) {
return Ok(String::new())
}

let target_node = graph.node(*target_index);

let mut imports = target_node.imports().clone();
Expand All @@ -219,7 +225,7 @@ impl ProjectPathsConfig {

for import in imports.iter() {
let import_path = self.resolve_import(target_dir, import.data())?;
let import_content = self.flatten_node(&import_path, graph, true, true)?;
let import_content = self.flatten_node(&import_path, graph, imported, true, true)?;
let import_content = import_content.trim().as_bytes().to_owned();
let import_content_len = import_content.len() as isize;
let (start, end) = import.loc_by_offset(offset);
Expand All @@ -230,6 +236,7 @@ impl ProjectPathsConfig {
let result = String::from_utf8(content).map_err(|err| {
SolcError::msg(format!("failed to convert extended bytes to string: {}", err))
})?;
imported.push(*target_index);

Ok(result)
}
Expand Down
4 changes: 4 additions & 0 deletions ethers-solc/test-data/flatten-sample/contracts/Bar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

contract Bar {}
6 changes: 6 additions & 0 deletions ethers-solc/test-data/flatten-sample/contracts/Foo.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

import { Bar } from './Bar.sol';

contract Foo {}
7 changes: 7 additions & 0 deletions ethers-solc/test-data/flatten-sample/contracts/FooBar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

import { Bar } from './Bar.sol';
import { Foo } from './Foo.sol';

contract FooBar {}
17 changes: 17 additions & 0 deletions ethers-solc/tests/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,20 @@ fn can_flatten_file_in_dapp_sample() {
assert!(result.find("contract Dapp").is_some());
assert!(result.find("contract DappTest").is_some());
}

#[test]
fn can_flatten_file_with_duplicates() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/flatten-sample");
let paths = ProjectPathsConfig::builder().sources(root.join("contracts"));
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();

let target = root.join("contracts/FooBar.sol");

let result = project.flatten(&target);
assert!(result.is_ok());

let result = result.unwrap();
assert!(result.matches("contract Foo {").collect::<Vec<_>>().len() == 1);
assert!(result.matches("contract Bar {").collect::<Vec<_>>().len() == 1);
assert!(result.matches("contract FooBar {").collect::<Vec<_>>().len() == 1);
}