From de0cf1c22dd19aac2756e86410bdfe7b2cf0010f Mon Sep 17 00:00:00 2001 From: xermicus Date: Wed, 7 Oct 2026 11:32:38 +0200 Subject: [PATCH 1/2] combined-json: fix a confusion with colons in the source path Signed-off-by: xermicus --- crates/resolc/src/build/mod.rs | 4 +- crates/resolc/src/cli_utils.rs | 21 ++++++++++- crates/resolc/src/tests/cli/combined_json.rs | 37 ++++++++++++++++++- .../src/tests/data/solidity/two_contracts.sol | 14 +++++++ 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 crates/resolc/src/tests/data/solidity/two_contracts.sol diff --git a/crates/resolc/src/build/mod.rs b/crates/resolc/src/build/mod.rs index fbfd21aee..c991244b1 100644 --- a/crates/resolc/src/build/mod.rs +++ b/crates/resolc/src/build/mod.rs @@ -304,9 +304,7 @@ impl Build { /// # Panics /// If the path does not contain a colon. fn normalize_full_path(path: &str) -> String { - let mut iterator = path.split(':'); - let path = iterator.next().expect("Always exists"); - let name = iterator.next().expect("Always exists"); + let (path, name) = path.rsplit_once(':').expect("Always exists"); let path = PathBuf::from(path); let mut full_path = path diff --git a/crates/resolc/src/cli_utils.rs b/crates/resolc/src/cli_utils.rs index 25164bc26..8a244be5d 100644 --- a/crates/resolc/src/cli_utils.rs +++ b/crates/resolc/src/cli_utils.rs @@ -2,7 +2,7 @@ use std::{ fs::File, - path::PathBuf, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -38,6 +38,8 @@ pub const SOLIDITY_LIBRARY_CALL_CONTRACT_PATH: &str = "src/tests/data/solidity/l pub const SOLIDITY_COLLISION_NESTED_PATH: &str = "src/tests/data/solidity/collision/a/c.sol"; /// The paritytech/revive#622 reproducer contract at `a_c.sol`. pub const SOLIDITY_COLLISION_UNDERSCORE_PATH: &str = "src/tests/data/solidity/collision/a_c.sol"; +/// Two contracts named `First` and `Second` returning `1` and `2`. +pub const SOLIDITY_TWO_CONTRACTS_PATH: &str = "src/tests/data/solidity/two_contracts.sol"; /// The verbatim reproducer from paritytech/revive#632: two contracts calling the same library. pub const SOLIDITY_LINK_INDEPENDENT_OBJECTS_PATH: &str = "src/tests/data/solidity/link_independent_objects.sol"; @@ -210,6 +212,23 @@ pub fn execute_resolc(arguments: &[&str]) -> CommandResult { execute_command("resolc", arguments, None) } +/// Executes the `resolc` command with the given `arguments` inside the working `directory`. +pub fn execute_resolc_in_directory(arguments: &[&str], directory: &Path) -> CommandResult { + let result = Command::new("resolc") + .args(arguments) + .current_dir(directory) + .stdin(Stdio::null()) + .output() + .unwrap(); + + CommandResult { + stdout: String::from_utf8_lossy(&result.stdout).to_string(), + stderr: String::from_utf8_lossy(&result.stderr).to_string(), + success: result.status.success(), + code: result.status.code().unwrap(), + } +} + /// Executes the `resolc` command with the given `arguments` and file path passed to `stdin`. pub fn execute_resolc_with_stdin_input(arguments: &[&str], stdin_file_path: &str) -> CommandResult { execute_command("resolc", arguments, Some(stdin_file_path)) diff --git a/crates/resolc/src/tests/cli/combined_json.rs b/crates/resolc/src/tests/cli/combined_json.rs index 33ea045cb..68d3800a1 100644 --- a/crates/resolc/src/tests/cli/combined_json.rs +++ b/crates/resolc/src/tests/cli/combined_json.rs @@ -4,8 +4,9 @@ use revive_solc_json_interface::{combined_json::CombinedJson, CombinedJsonInvali use crate::cli_utils::{ assert_command_failure, assert_command_success, assert_equal_exit_codes, execute_resolc, - execute_solc, SOLIDITY_CONTRACT_PATH, SOLIDITY_INCLUDED_IMPORT_PATH, - SOLIDITY_REMAPPED_IMPORT_PATH, YUL_CONTRACT_PATH, + execute_resolc_in_directory, execute_solc, SOLIDITY_CONTRACT_PATH, + SOLIDITY_INCLUDED_IMPORT_PATH, SOLIDITY_REMAPPED_IMPORT_PATH, SOLIDITY_TWO_CONTRACTS_PATH, + YUL_CONTRACT_PATH, }; use crate::ResolcVersion; @@ -194,3 +195,35 @@ fn resolves_imports_with_base_and_include_paths() { ], ); } + +/// Contracts in a source whose path contains a colon keep their own entries. +#[test] +fn keeps_contracts_of_source_path_with_colon_apart() { + const SOURCE_PATH_WITH_COLON: &str = "directory:file.sol"; + + let directory = tempfile::tempdir().unwrap(); + std::fs::copy( + SOLIDITY_TWO_CONTRACTS_PATH, + directory.path().join(SOURCE_PATH_WITH_COLON), + ) + .unwrap(); + + let result = execute_resolc_in_directory( + &[SOURCE_PATH_WITH_COLON, JSON_OPTION, "bin"], + directory.path(), + ); + assert_command_success(&result, "Compiling a source path with a colon"); + + let combined_json: serde_json::Value = + serde_json::from_str(&result.stdout).expect("Combined JSON output should deserialize"); + let bytecode = |name: &str| { + combined_json["contracts"][format!("{SOURCE_PATH_WITH_COLON}:{name}")]["bin"] + .as_str() + .unwrap_or_else(|| panic!("Combined JSON output should contain `{name}` bytecode")) + .to_owned() + }; + let first = bytecode("First"); + let second = bytecode("Second"); + assert!(!first.is_empty() && !second.is_empty()); + assert_ne!(first, second); +} diff --git a/crates/resolc/src/tests/data/solidity/two_contracts.sol b/crates/resolc/src/tests/data/solidity/two_contracts.sol new file mode 100644 index 000000000..540131267 --- /dev/null +++ b/crates/resolc/src/tests/data/solidity/two_contracts.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract First { + function value() external pure returns (uint256) { + return 1; + } +} + +contract Second { + function value() external pure returns (uint256) { + return 2; + } +} From 1d49e68a088bede53cc4637eb1eb36948c6e5523 Mon Sep 17 00:00:00 2001 From: xermicus Date: Thu, 8 Oct 2026 17:21:25 +0200 Subject: [PATCH 2/2] review Signed-off-by: xermicus --- crates/resolc/src/cli_utils.rs | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/crates/resolc/src/cli_utils.rs b/crates/resolc/src/cli_utils.rs index ff2f447e7..9042d32fa 100644 --- a/crates/resolc/src/cli_utils.rs +++ b/crates/resolc/src/cli_utils.rs @@ -218,28 +218,16 @@ pub fn execute_resolc(arguments: &[&str]) -> CommandResult { execute_command("resolc", arguments, None) } -/// Executes the `resolc` command with the given `arguments` inside the working `directory`. -pub fn execute_resolc_in_directory(arguments: &[&str], directory: &Path) -> CommandResult { - let result = Command::new("resolc") - .args(arguments) - .current_dir(directory) - .stdin(Stdio::null()) - .output() - .unwrap(); - - CommandResult { - stdout: String::from_utf8_lossy(&result.stdout).to_string(), - stderr: String::from_utf8_lossy(&result.stderr).to_string(), - success: result.status.success(), - code: result.status.code().unwrap(), - } -} - /// Executes the `resolc` command with the given `arguments` and file path passed to `stdin`. pub fn execute_resolc_with_stdin_input(arguments: &[&str], stdin_file_path: &str) -> CommandResult { execute_command("resolc", arguments, Some(stdin_file_path)) } +/// Executes the `resolc` command with the given `arguments` inside `directory`. +pub fn execute_resolc_in_directory(arguments: &[&str], directory: &Path) -> CommandResult { + execute_command_in_directory("resolc", arguments, None, Some(directory)) +} + /// Executes the `solc` command with the given `arguments`. pub fn execute_solc(arguments: &[&str]) -> CommandResult { execute_command(SolcCompiler::DEFAULT_EXECUTABLE_NAME, arguments, None) @@ -259,6 +247,16 @@ pub fn execute_command( command: &str, arguments: &[&str], stdin_file_path: Option<&str>, +) -> CommandResult { + execute_command_in_directory(command, arguments, stdin_file_path, None) +} + +/// Executes the `command` like [`execute_command`], inside `directory` if given. +pub fn execute_command_in_directory( + command: &str, + arguments: &[&str], + stdin_file_path: Option<&str>, + directory: Option<&Path>, ) -> CommandResult { log::trace!( "executing command: '{command} {}{}'", @@ -272,7 +270,11 @@ pub fn execute_command( Some(path) => Stdio::from(File::open(path).unwrap()), None => Stdio::null(), }; - let result = Command::new(command) + let mut command = Command::new(command); + if let Some(directory) = directory { + command.current_dir(directory); + } + let result = command .args(arguments) .stdin(stdin_config) .stdout(Stdio::piped())