From d5a451215be9e93c6af3113c70467b6a2c4e79e7 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 10 Nov 2025 20:34:52 +0200 Subject: [PATCH 1/2] fix(forge): preprocess mockes declared in same test file --- Cargo.lock | 17 ++---- Cargo.toml | 2 +- crates/common/src/preprocessor/deps.rs | 74 +++++++++++++--------- crates/forge/tests/cli/test_optimizer.rs | 78 +++++++++++++++++++++++- 4 files changed, 130 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18abc7fda708f..babacff131399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4692,8 +4692,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5767bfd01a240b57e6eb540e233b248d2ecb554d60b7936574cd077c0bd4c15e" +source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4703,7 +4702,7 @@ dependencies = [ "foundry-compilers-artifacts", "foundry-compilers-core", "fs_extra", - "itertools 0.14.0", + "itertools 0.13.0", "path-slash", "rand 0.9.2", "rayon", @@ -4724,8 +4723,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b35da40bb333baff6f4533431c050a94857a4d1a379365085da403938fc9edf" +source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -4734,8 +4732,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff50fa9f518e96f80c77d1596122bb41c6fb9f1ec8d93e3127e2cf7a4726faa" +source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4755,8 +4752,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34764d842fcf3aee77dc892beba3f12d7d5cebe196caf2050302caa986de8914" +source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4770,8 +4766,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bcd9ff6881e2f5acd8544d490cc91d2c1d47c59c1e9337ea2ade66ce7e1800" +source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index d06e6a0c2f23a..e7b790bb70d3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -446,7 +446,7 @@ rexpect = { git = "https://github.com/rust-cli/rexpect", rev = "2ed0b1898d7edaf6 ## foundry # foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers.git", rev = "f5b46b2" } -# foundry-compilers = { git = "https://github.com/foundry-rs/compilers.git", branch = "main" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers.git", rev = "1d5872a" } # foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "be95912" } # solar diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs index 71d7df1da452e..25228aba18c82 100644 --- a/crates/common/src/preprocessor/deps.rs +++ b/crates/common/src/preprocessor/deps.rs @@ -33,41 +33,54 @@ impl PreprocessorDependencies { ) -> Self { let mut preprocessed_contracts = BTreeMap::new(); let mut referenced_contracts = HashSet::new(); - for contract_id in gcx.hir.contract_ids() { - let contract = gcx.hir.contract(contract_id); - let source = gcx.hir.source(contract.source); + let mut current_mocks = HashSet::new(); + + // Helper closure for iterating candidate contracts to preprocess (tests and scripts). + let candidate_contracts = || { + gcx.hir.contract_ids().filter_map(|id| { + let contract = gcx.hir.contract(id); + let source = gcx.hir.source(contract.source); + let FileName::Real(path) = &source.file.name else { + return None; + }; - let FileName::Real(path) = &source.file.name else { - continue; - }; + if !paths.contains(path) { + trace!("{} is not test or script", path.display()); + return None; + } - // Collect dependencies only for tests and scripts. - if !paths.contains(path) { - let path = path.display(); - trace!("{path} is not test or script"); - continue; - } + Some((id, contract, source, path)) + }) + }; - // Do not collect dependencies for mock contracts. Walk through base contracts and - // check if they're from src dir. - if contract.linearized_bases.iter().any(|base_contract_id| { - let base_contract = gcx.hir.contract(*base_contract_id); - let FileName::Real(path) = &gcx.hir.source(base_contract.source).file.name else { - return false; - }; - path.starts_with(src_dir) + // Collect current mocks. + for (_, contract, _, path) in candidate_contracts() { + if contract.linearized_bases.iter().any(|base_id| { + let base = gcx.hir.contract(*base_id); + matches!( + &gcx.hir.source(base.source).file.name, + FileName::Real(base_path) if base_path.starts_with(src_dir) + ) }) { - // Record mock contracts to be evicted from preprocessed cache. - mocks.insert(root_dir.join(path)); - let path = path.display(); - trace!("found mock contract {path}"); + let mock_path = root_dir.join(path); + trace!("found mock contract {}", mock_path.display()); + current_mocks.insert(mock_path); + } + } + + // Collect dependencies for non-mock test/script contracts. + for (contract_id, contract, source, path) in candidate_contracts() { + let full_path = root_dir.join(path); + + if current_mocks.contains(&full_path) { + trace!("{} is a mock, skipping", path.display()); continue; - } else { - // Make sure current contract is not in list of mocks (could happen when a contract - // which used to be a mock is refactored to a non-mock implementation). - mocks.remove(&root_dir.join(path)); } + // Make sure current contract is not in list of mocks (could happen when a contract + // which used to be a mock is refactored to a non-mock implementation). + mocks.remove(&full_path); + let mut deps_collector = BytecodeDependencyCollector::new(gcx, source.file.src.as_str(), src_dir); // Analyze current contract. @@ -76,9 +89,14 @@ impl PreprocessorDependencies { if !deps_collector.dependencies.is_empty() { preprocessed_contracts.insert(contract_id, deps_collector.dependencies); } + // Record collected referenced contract ids. referenced_contracts.extend(deps_collector.referenced_contracts); } + + // Add current mocks. + mocks.extend(current_mocks); + Self { preprocessed_contracts, referenced_contracts } } } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 560246ca3fd6b..a8cd15b807114 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -568,7 +568,7 @@ contract Counter { // compiled and both tests fail. cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" ... -Compiling 2 files with [..] +Compiling 3 files with [..] ... [FAIL: assertion failed: 12347 != 1] test_Increment() (gas: [..]) [FAIL: assertion failed: 12345 != 1] test_SetNumber() (gas: [..]) @@ -716,6 +716,82 @@ Compiling 2 files with [..] "#]]); }); +// +// - CounterMock contract is Counter contract +// - CounterMock declared in CounterTest +// +// ├── src +// │ └── Counter.sol +// └── test +// ├── Counter.t.sol +forgetest_init!(preprocess_mock_declared_in_test_contract, |prj, cmd| { + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + function add(uint256 x, uint256 y) public pure returns (uint256) { + return x + y; + } +} + "#, + ); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; + +contract CounterMock is Counter {} + +contract CounterTest is Test { + function test_add() public { + CounterMock impl = new CounterMock(); + assertEq(impl.add(2, 2), 4); + } +} + "#, + ); + // 20 files plus one mock file are compiled on first run. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 21 files with [..] +... + +"#]]); + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +No files changed, compilation skipped +... + +"#]]); + + // Change Counter implementation to fail tests. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + function add(uint256 x, uint256 y) public pure returns (uint256) { + return x + y + 1; + } +} + "#, + ); + // Assert that Counter and CounterTest files are compiled and tests fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 2 files with [..] +... +[FAIL: assertion failed: 5 != 4] test_add() (gas: [..]) +... + +"#]]); +}); + // ├── src // │ ├── CounterA.sol // │ ├── CounterB.sol From 823b0d63fe34f879083cdcc2edf2f8590b0c2181 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 12 Nov 2025 11:26:32 +0200 Subject: [PATCH 2/2] Bump compilers version --- Cargo.lock | 25 +++++++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index babacff131399..5ed2fcdcdaea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4691,8 +4691,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.19.5" -source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2b71f446c308daa68016cd51397eeeacec940a88985ac5c97cd848a90a7120" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4722,8 +4723,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.19.5" -source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1917c800c1bfa634f4cdb1d9fc5ab2b178e301986d59d42cc0912ff5b8eeaa4" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -4731,8 +4733,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.19.5" -source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93771b9ec1b582c66de791a7505062815a921f120918bf34a066a6000c9aee81" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4751,8 +4754,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.19.5" -source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be462f9144604965e42c7afcf2e04d0dc5aca1b51a133fe72cd3feaf3cd81579" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4765,8 +4769,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.19.5" -source = "git+https://github.com/foundry-rs/compilers.git?rev=1d5872a#1d5872a0f69b42addf80a1f1b88ca8e8b9adcace" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afc3767284e674e270ba31b94befa7faf976c100b3489e5c79f4a32d87a84a4" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index e7b790bb70d3d..d9067dac52f9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -223,7 +223,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.22.0", default-features = false } -foundry-compilers = { version = "0.19.5", default-features = false, features = [ +foundry-compilers = { version = "0.19.6", default-features = false, features = [ "rustls", "svm-solc", ] } @@ -446,7 +446,7 @@ rexpect = { git = "https://github.com/rust-cli/rexpect", rev = "2ed0b1898d7edaf6 ## foundry # foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers.git", rev = "f5b46b2" } -foundry-compilers = { git = "https://github.com/foundry-rs/compilers.git", rev = "1d5872a" } +# foundry-compilers = { git = "https://github.com/foundry-rs/compilers.git", branch = "main" } # foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "be95912" } # solar