From 009e9a8ee5ff9f78d7b22ae7718f3bb5889c2054 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 4 Mar 2025 16:10:03 +0200 Subject: [PATCH 01/27] feat(forge): optimize compilation through preprocessing and caching --- Cargo.lock | 74 ++++++++++++++++++++++---------- Cargo.toml | 5 +++ crates/common/src/compile.rs | 34 ++++++++++++--- crates/config/src/lib.rs | 3 ++ crates/forge/bin/cmd/test/mod.rs | 6 ++- crates/forge/tests/cli/config.rs | 3 ++ 6 files changed, 96 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58bd958639ac7..9aacdecd2b4ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3818,8 +3818,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8f0bab060fd7c1764c4be2563e6933d39ec8c2b8a8d6c08aaf45ab29d08310" +source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3842,6 +3841,7 @@ dependencies = [ "serde_json", "sha2", "solar-parse", + "solar-sema", "svm-rs", "svm-rs-builds", "tempfile", @@ -3855,8 +3855,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102dd131e939d80cc5c85214d2f0f6ba20ed75cf098019c4d995791b4ebae05" +source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3865,8 +3864,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9ef02de4fda04ae3ed098afb6e16edd742d1073f972197a4566836b453bdcd" +source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3889,8 +3887,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd9d33cbeda448af917105920fefdc3ac42f9ffdaa2d840d58207db7807ea29" +source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3904,8 +3901,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746d121f7b86b84b20e582a27a56c49435768ad3b8005e9afeaf68b53a77fb5c" +source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" dependencies = [ "alloy-primitives", "cfg-if", @@ -6298,6 +6294,18 @@ version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "once_map" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd2cae3bec3936bbed1ccc5a3343b3738858182419f9c0522c7260c80c430b0" +dependencies = [ + "ahash", + "hashbrown 0.15.2", + "parking_lot", + "stable_deref_trait", +] + [[package]] name = "op-alloy-consensus" version = "0.10.7" @@ -8349,8 +8357,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3f6c4a476a16dcd36933a70ecdb0a807f8949cc5f3c4c1984e3748666bd714" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "alloy-primitives", "bumpalo", @@ -8368,8 +8375,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40434a61f2c14a9e3777fbc478167bddee9828532fc26c57e416e9277916b09" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "strum 0.26.3", ] @@ -8377,8 +8383,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d07263243b313296eca18f18eda3a190902dc3284bf67ceff29b8b54dac3e6" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "bumpalo", "index_vec", @@ -8392,8 +8397,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a87009b6989b2cc44d8381e3b86ff3b90280d54a60321919b6416214cd602f3" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "annotate-snippets", "anstream", @@ -8420,8 +8424,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970d7c774741f786d62cab78290e47d845b0b9c0c9d094a1642aced1d7946036" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "proc-macro2", "quote", @@ -8431,8 +8434,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e1e2d07fae218aca1b4cca81216e5c9ad7822516d48a28f11e2eaa8ffa5b249" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8449,6 +8451,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "solar-sema" +version = "0.1.1" +source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "bitflags 2.9.0", + "bumpalo", + "derive_more 1.0.0", + "either", + "once_map", + "paste", + "rayon", + "scc", + "serde", + "serde_json", + "solar-ast", + "solar-data-structures", + "solar-interface", + "solar-macros", + "solar-parse", + "strum 0.26.3", + "thread_local", + "tracing", + "typed-arena", +] + [[package]] name = "soldeer-commands" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index db025a7435400..0af3383ab0503 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -316,6 +316,11 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "4342f82" } + +solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } + ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } # alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index e7e37d2dbd55d..ee11a128d1491 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -16,6 +16,8 @@ use foundry_compilers::{ Compiler, }, info::ContractInfo as CompilerContractInfo, + preprocessor::TestOptimizerPreprocessor, + project::Preprocessor, report::{BasicStdoutReporter, NoReporter, Report}, solc::SolcSettings, Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, SolcConfig, @@ -59,6 +61,9 @@ pub struct ProjectCompiler { /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, + + /// Whether to compile with preprocessed cache for tests and scripts. + preprocess_cache: bool, } impl Default for ProjectCompiler { @@ -81,6 +86,7 @@ impl ProjectCompiler { bail: None, ignore_eip_3860: false, files: Vec::new(), + preprocess_cache: false, } } @@ -134,11 +140,21 @@ impl ProjectCompiler { self } + /// Sets if compiler should use preprocessed cache. + #[inline] + pub fn preprocess_cache(mut self, preprocess: bool) -> Self { + self.preprocess_cache = preprocess; + self + } + /// Compiles the project. pub fn compile>( mut self, project: &Project, - ) -> Result> { + ) -> Result> + where + TestOptimizerPreprocessor: Preprocessor, + { self.project_root = project.root().clone(); // TODO: Avoid process::exit @@ -150,6 +166,7 @@ impl ProjectCompiler { // Taking is fine since we don't need these in `compile_with`. let files = std::mem::take(&mut self.files); + let preprocess = self.preprocess_cache; self.compile_with(|| { let sources = if !files.is_empty() { Source::read_all(files)? @@ -157,9 +174,13 @@ impl ProjectCompiler { project.paths.read_input_files()? }; - foundry_compilers::project::ProjectCompiler::with_sources(project, sources)? - .compile() - .map_err(Into::into) + let compiler = + foundry_compilers::project::ProjectCompiler::with_sources(project, sources)?; + if preprocess { + compiler.with_preprocessor(TestOptimizerPreprocessor).compile().map_err(Into::into) + } else { + compiler.compile().map_err(Into::into) + } }) } @@ -491,7 +512,10 @@ pub fn compile_target>( target_path: &Path, project: &Project, quiet: bool, -) -> Result> { +) -> Result> +where + TestOptimizerPreprocessor: Preprocessor, +{ ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 62df691ac2ef6..8ade2a8c23680 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -196,6 +196,8 @@ pub struct Config { pub libraries: Vec, /// whether to enable cache pub cache: bool, + /// whether to enable preprocessed cache for tests + pub preprocess_cache: bool, /// where the cache is stored if enabled pub cache_path: PathBuf, /// where the gas snapshots are stored @@ -2319,6 +2321,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, + preprocess_cache: false, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 709908d6698fd..a860d9edea36c 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -305,8 +305,10 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; - let compiler = - ProjectCompiler::new().quiet(shell::is_json() || self.junit).files(sources_to_compile); + let compiler = ProjectCompiler::new() + .preprocess_cache(config.preprocess_cache) + .quiet(shell::is_json() || self.junit) + .files(sources_to_compile); let output = compiler.compile(&project)?; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 23b72a86526ff..deff9d4a29a9d 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,6 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, + preprocess_cache: false, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -959,6 +960,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true +preprocess_cache = false cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1116,6 +1118,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, + "preprocess_cache": false, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, From b99643c799195bf9251c11b13c2cda84e6c626d1 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 5 Mar 2025 12:50:49 +0200 Subject: [PATCH 02/27] Add various test scenarios --- Cargo.lock | 12 +- Cargo.toml | 2 +- crates/common/src/compile.rs | 10 +- crates/config/src/lib.rs | 4 +- crates/forge/bin/cmd/build.rs | 1 + crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/config.rs | 6 +- crates/forge/tests/cli/main.rs | 1 + crates/forge/tests/cli/test_optimizer.rs | 945 +++++++++++++++++++++++ 9 files changed, 965 insertions(+), 18 deletions(-) create mode 100644 crates/forge/tests/cli/test_optimizer.rs diff --git a/Cargo.lock b/Cargo.lock index 9aacdecd2b4ef..548aa301cfd51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3818,7 +3818,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" +source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3831,7 +3831,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.14.0", + "itertools 0.13.0", "md-5", "path-slash", "rand 0.8.5", @@ -3855,7 +3855,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" +source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3864,7 +3864,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" +source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3887,7 +3887,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" +source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3901,7 +3901,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=4342f82#4342f82e03c97922e2900ef88beacc17e6fa9922" +source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 0af3383ab0503..58cfaf36c2250 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -316,7 +316,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "4342f82" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "c4ec52c" } solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index ee11a128d1491..7b07771689a1f 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -63,7 +63,7 @@ pub struct ProjectCompiler { files: Vec, /// Whether to compile with preprocessed cache for tests and scripts. - preprocess_cache: bool, + optimize_tests: bool, } impl Default for ProjectCompiler { @@ -86,7 +86,7 @@ impl ProjectCompiler { bail: None, ignore_eip_3860: false, files: Vec::new(), - preprocess_cache: false, + optimize_tests: false, } } @@ -142,8 +142,8 @@ impl ProjectCompiler { /// Sets if compiler should use preprocessed cache. #[inline] - pub fn preprocess_cache(mut self, preprocess: bool) -> Self { - self.preprocess_cache = preprocess; + pub fn optimize_tests(mut self, preprocess: bool) -> Self { + self.optimize_tests = preprocess; self } @@ -166,7 +166,7 @@ impl ProjectCompiler { // Taking is fine since we don't need these in `compile_with`. let files = std::mem::take(&mut self.files); - let preprocess = self.preprocess_cache; + let preprocess = self.optimize_tests; self.compile_with(|| { let sources = if !files.is_empty() { Source::read_all(files)? diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8ade2a8c23680..0e4b2e8f15fce 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -197,7 +197,7 @@ pub struct Config { /// whether to enable cache pub cache: bool, /// whether to enable preprocessed cache for tests - pub preprocess_cache: bool, + pub optimize_tests: bool, /// where the cache is stored if enabled pub cache_path: PathBuf, /// where the gas snapshots are stored @@ -2321,7 +2321,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, - preprocess_cache: false, + optimize_tests: false, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 133e2f9ad70ca..9e7572d33523e 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -92,6 +92,7 @@ impl BuildArgs { let format_json = shell::is_json(); let compiler = ProjectCompiler::new() .files(files) + .optimize_tests(config.optimize_tests) .print_names(self.names) .print_sizes(self.sizes) .ignore_eip_3860(self.ignore_eip_3860) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index a860d9edea36c..8f4e1026d9e19 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -306,7 +306,7 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; let compiler = ProjectCompiler::new() - .preprocess_cache(config.preprocess_cache) + .optimize_tests(config.optimize_tests) .quiet(shell::is_json() || self.junit) .files(sources_to_compile); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index deff9d4a29a9d..80ef5b81930db 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,7 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, - preprocess_cache: false, + optimize_tests: false, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -960,7 +960,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true -preprocess_cache = false +optimize_tests = false cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1118,7 +1118,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, - "preprocess_cache": false, + "optimize_tests": false, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index cd12f45ab7143..2b6a6998eb228 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -30,3 +30,4 @@ mod verify_bytecode; mod version; mod ext_integration; +mod test_optimizer; diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs new file mode 100644 index 0000000000000..3351996e157dc --- /dev/null +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -0,0 +1,945 @@ +//! Tests for the `forge test` with preprocessed cache. + +// Test cache is invalidated when `forge build` if optimize test option toggled. +forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { + prj.update_config(|config| { + config.optimize_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 22 files with [..] +... + +"#]]); + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +No files changed, compilation skipped +... + +"#]]); + + // Toggle test optimizer off. + prj.update_config(|config| { + config.optimize_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 22 files with [..] +... + +"#]]); +}); + +// Test cache is invalidated when `forge test` if optimize test option toggled. +forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { + prj.update_config(|config| { + config.optimize_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 20 files with [..] +... + +"#]]); + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +No files changed, compilation skipped +... + +"#]]); + + // Toggle test optimizer off. + prj.update_config(|config| { + config.optimize_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 20 files with [..] +... + +"#]]); +}); + +// Counter contract without interface instantiated in CounterTest +// +// ├── src +// │ └── Counter.sol +// └── test +// └── Counter.t.sol +forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_SetNumber() public { + counter.setNumber(1); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + // All 20 files are compiled on first run. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 20 files with [..] +... + +"#]]); + + // Change Counter implementation to fail both tests. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = 12345; + } + + function increment() public { + number++; + number++; + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and both tests fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 12347 != 1] test_Increment() (gas: [..]) +[FAIL: assertion failed: 12345 != 1] test_SetNumber() (gas: [..]) +... + +"#]]); + + // Change Counter implementation to fail single test. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = 1; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and only one test fails. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 2 != 1] test_Increment() (gas: [..]) +[PASS] test_SetNumber() (gas: [..]) +... + +"#]]); +}); + +// Counter contract with interface instantiated in CounterTest +// +// ├── src +// │ ├── Counter.sol +// │ └── interface +// │ └── CounterIf.sol +// └── test +// └── Counter.t.sol +forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "interface/CounterIf.sol", + r#" +interface CounterIf { + function number() external returns (uint256); + + function setNumber(uint256 newNumber) external; + + function increment() external; +} + "#, + ) + .unwrap(); + prj.add_source( + "Counter.sol", + r#" +import {CounterIf} from "./interface/CounterIf.sol"; +contract Counter is CounterIf { + uint256 public number; + uint256 public anotherNumber; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_SetNumber() public { + counter.setNumber(1); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + // All 21 files are compiled on first run. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 21 files with [..] +... + +"#]]); + + // Change only CounterIf interface. + prj.add_source( + "interface/CounterIf.sol", + r#" +interface CounterIf { + function anotherNumber() external returns (uint256); + + function number() external returns (uint256); + + function setNumber(uint256 newNumber) external; + + function increment() external; +} + "#, + ) + .unwrap(); + // All 3 files (interface, implementation and test) are compiled. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 3 files with [..] +... + +"#]]); + + // Change Counter implementation to fail both tests. + prj.add_source( + "Counter.sol", + r#" +import {CounterIf} from "./interface/CounterIf.sol"; +contract Counter is CounterIf { + uint256 public number; + uint256 public anotherNumber; + + function setNumber(uint256 newNumber) public { + number = 12345; + } + + function increment() public { + number++; + number++; + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and both tests fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 12347 != 1] test_Increment() (gas: [..]) +[FAIL: assertion failed: 12345 != 1] test_SetNumber() (gas: [..]) +... + +"#]]); +}); + +// - Counter contract instantiated in CounterMock +// - CounterMock instantiated in CounterTest +// +// ├── src +// │ └── Counter.sol +// └── test +// ├── Counter.t.sol +// └── mock +// └── CounterMock.sol +forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; + +contract CounterMock { + Counter counter = new Counter(); + + function setNumber(uint256 newNumber) public { + counter.setNumber(newNumber); + } + + function increment() public { + counter.increment(); + } + + function number() public returns (uint256) { + return counter.number(); + } +} + "#, + ) + .unwrap(); + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {CounterMock} from "./mock/CounterMock.sol"; + +contract CounterTest is Test { + CounterMock public counter; + + function setUp() public { + counter = new CounterMock(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_SetNumber() public { + counter.setNumber(1); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + // 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 [..] +... + +"#]]); + + // Change Counter contract implementation to fail both tests. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = 12345; + } + + function increment() public { + number++; + number++; + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and both tests fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 12347 != 1] test_Increment() (gas: [..]) +[FAIL: assertion failed: 12345 != 1] test_SetNumber() (gas: [..]) +... + +"#]]); + + // Change CounterMock contract implementation to pass both tests. + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; + +contract CounterMock { + Counter counter = new Counter(); + + function setNumber(uint256 newNumber) public { + } + + function increment() public { + } + + function number() public returns (uint256) { + return 1; + } +} + "#, + ) + .unwrap(); + // Assert that mock and test files are compiled and no test fails. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 2 files with [..] +... +[PASS] test_Increment() (gas: [..]) +[PASS] test_SetNumber() (gas: [..]) +... + +"#]]); +}); + +// - CounterMock contract is Counter contract +// - CounterMock instantiated in CounterTest +// +// ├── src +// │ └── Counter.sol +// └── test +// ├── Counter.t.sol +// └── mock +// └── CounterMock.sol +forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Counter} from "src/Counter.sol"; + +contract CounterMock is Counter { +} + "#, + ) + .unwrap(); + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {CounterMock} from "./mock/CounterMock.sol"; + +contract CounterTest is Test { + CounterMock public counter; + + function setUp() public { + counter = new CounterMock(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_SetNumber() public { + counter.setNumber(1); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + // 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 [..] +... + +"#]]); + + // Change Counter contract implementation to fail both tests. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public virtual { + number = 12345; + } + + function increment() public virtual { + number++; + number++; + } +} + "#, + ) + .unwrap(); + // Assert Counter source contract and CounterTest test contract (as it imports mock) are + // compiled and both tests fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 2 files with [..] +... +[FAIL: assertion failed: 12347 != 1] test_Increment() (gas: [..]) +[FAIL: assertion failed: 12345 != 1] test_SetNumber() (gas: [..]) +... + +"#]]); + + // Change mock implementation to pass both tests. + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Counter} from "src/Counter.sol"; + +contract CounterMock is Counter { + function setNumber(uint256 newNumber) public override { + number = newNumber; + } + + function increment() public override { + number++; + } +} + "#, + ) + .unwrap(); + // Assert that CounterMock and CounterTest files are compiled and no test fails. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 2 files with [..] +... +[PASS] test_Increment() (gas: [..]) +[PASS] test_SetNumber() (gas: [..]) +... + +"#]]); +}); + +// - CounterMock contract is Counter contract +// - CounterMock instantiated in CounterTest +// +// ├── src +// │ └── Counter.sol +// └── test +// ├── Counter.t.sol +// └── mock +// └── CounterMock.sol +forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Counter} from "src/Counter.sol"; + +contract CounterMock is Counter { +} + "#, + ) + .unwrap(); + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {CounterMock} from "./mock/CounterMock.sol"; + +contract CounterTest is Test { + CounterMock public counter; + + function setUp() public { + counter = new CounterMock(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_SetNumber() public { + counter.setNumber(1); + assertEq(counter.number(), 1); + } +} + "#, + ) + .unwrap(); + // 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 mock implementation to fail tests, no inherit from Counter. + prj.add_test( + "mock/CounterMock.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; + +contract CounterMock { + uint256 public number; + function setNumber(uint256 newNumber) public { + number = 1234; + } + + function increment() public { + number = 5678; + } +} + "#, + ) + .unwrap(); + // Assert that CounterMock 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: 5678 != 1] test_Increment() (gas: [..]) +[FAIL: assertion failed: 1234 != 1] test_SetNumber() (gas: [..]) +... + +"#]]); +}); + +// ├── src +// │ ├── CounterA.sol +// │ ├── Counter.sol +// │ └── v1 +// │ └── Counter.sol +// └── test +// └── Counter.t.sol +forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimize_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "CounterA.sol", + r#" +contract CounterA { + uint256 public number; + address public owner; + + constructor(uint256 _newNumber, address _owner) { + number = _newNumber; + owner = _owner; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "v1/Counter.sol", + r#" +contract Counter { + uint256 public number; + + constructor(uint256 _number) { + number = _number; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; +import "src/CounterA.sol"; +import {Counter as CounterV1} from "src/v1/Counter.sol"; + +contract CounterTest is Test { + function test_Increment_In_Counter() public { + Counter counter = new Counter(); + counter.increment(); + assertEq(counter.number(), 1); + } + + function test_Increment_In_Counter_V1() public { + CounterV1 counter = new CounterV1(1234); + counter.increment(); + assertEq(counter.number(), 1235); + } + + function test_Increment_In_Counter_A() public { + CounterA counter = new CounterA(1234, address(this)); + counter.increment(); + assertEq(counter.number(), 1235); + } +} + "#, + ) + .unwrap(); + // 20 files plus one mock file are compiled on first run. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 22 files with [..] +... +[PASS] test_Increment_In_Counter() (gas: [..]) +[PASS] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_V1() (gas: [..]) +... + +"#]]); + + // Change v1/Counter to fail test. + prj.add_source( + "v1/Counter.sol", + r#" +contract Counter { + uint256 public number; + + constructor(uint256 _number) { + number = _number; + } + + function increment() public { + number = 12345; + } +} + "#, + ) + .unwrap(); + // Only v1/Counter should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[PASS] test_Increment_In_Counter() (gas: [..]) +[PASS] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) +... + +"#]]); + + // Change CounterA to fail test. + prj.add_source( + "CounterA.sol", + r#" +contract CounterA { + uint256 public number; + address public owner; + + constructor(uint256 _newNumber, address _owner) { + number = _newNumber; + owner = _owner; + } + + function increment() public { + number = 12345; + } +} + "#, + ) + .unwrap(); + // Only CounterA should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[PASS] test_Increment_In_Counter() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) +... + +"#]]); + + // Change Counter to fail test. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number = 12345; + } +} + "#, + ) + .unwrap(); + // Only Counter should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 12345 != 1] test_Increment_In_Counter() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) +... + +"#]]); +}); From ae939852a8d811255cbe64860406c108904635d4 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 5 Mar 2025 13:56:02 +0200 Subject: [PATCH 03/27] Changes after review: rename cache_tests, add build option --- crates/cli/src/opts/build/core.rs | 9 +++++++++ crates/common/src/compile.rs | 10 +++++----- crates/config/src/lib.rs | 4 ++-- crates/forge/bin/cmd/build.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/config.rs | 6 +++--- crates/forge/tests/cli/test_optimizer.rs | 20 ++++++++++---------- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index b9de478c3f237..fa2a86b40a1a0 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -34,6 +34,11 @@ pub struct BuildOpts { #[serde(skip)] pub no_cache: bool, + /// Enable preprocessed cache for tests. + #[arg(long, conflicts_with = "no_cache")] + #[serde(skip)] + pub cache_tests: bool, + /// Set pre-linked libraries. #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] #[serde(skip_serializing_if = "Vec::is_empty")] @@ -240,6 +245,10 @@ impl Provider for BuildOpts { dict.insert("cache".to_string(), false.into()); } + if self.cache_tests { + dict.insert("cache_tests".to_string(), true.into()); + } + if self.build_info { dict.insert("build_info".to_string(), self.build_info.into()); } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 7b07771689a1f..3b2faf77f57db 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -63,7 +63,7 @@ pub struct ProjectCompiler { files: Vec, /// Whether to compile with preprocessed cache for tests and scripts. - optimize_tests: bool, + cache_tests: bool, } impl Default for ProjectCompiler { @@ -86,7 +86,7 @@ impl ProjectCompiler { bail: None, ignore_eip_3860: false, files: Vec::new(), - optimize_tests: false, + cache_tests: false, } } @@ -142,8 +142,8 @@ impl ProjectCompiler { /// Sets if compiler should use preprocessed cache. #[inline] - pub fn optimize_tests(mut self, preprocess: bool) -> Self { - self.optimize_tests = preprocess; + pub fn cache_tests(mut self, preprocess: bool) -> Self { + self.cache_tests = preprocess; self } @@ -166,7 +166,7 @@ impl ProjectCompiler { // Taking is fine since we don't need these in `compile_with`. let files = std::mem::take(&mut self.files); - let preprocess = self.optimize_tests; + let preprocess = self.cache_tests; self.compile_with(|| { let sources = if !files.is_empty() { Source::read_all(files)? diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0e4b2e8f15fce..1435e45b599c6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -197,7 +197,7 @@ pub struct Config { /// whether to enable cache pub cache: bool, /// whether to enable preprocessed cache for tests - pub optimize_tests: bool, + pub cache_tests: bool, /// where the cache is stored if enabled pub cache_path: PathBuf, /// where the gas snapshots are stored @@ -2321,7 +2321,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, - optimize_tests: false, + cache_tests: false, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 9e7572d33523e..06aed95b1d37d 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -92,7 +92,7 @@ impl BuildArgs { let format_json = shell::is_json(); let compiler = ProjectCompiler::new() .files(files) - .optimize_tests(config.optimize_tests) + .cache_tests(config.cache_tests) .print_names(self.names) .print_sizes(self.sizes) .ignore_eip_3860(self.ignore_eip_3860) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 8f4e1026d9e19..9b07c6c4d09ca 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -306,7 +306,7 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; let compiler = ProjectCompiler::new() - .optimize_tests(config.optimize_tests) + .cache_tests(config.cache_tests) .quiet(shell::is_json() || self.junit) .files(sources_to_compile); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 80ef5b81930db..a95ed06469002 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,7 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, - optimize_tests: false, + cache_tests: false, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -960,7 +960,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true -optimize_tests = false +cache_tests = false cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1118,7 +1118,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, - "optimize_tests": false, + "cache_tests": false, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 3351996e157dc..cbaaac26405a2 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -3,7 +3,7 @@ // Test cache is invalidated when `forge build` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); // All files are built with optimized tests. cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" @@ -22,7 +22,7 @@ No files changed, compilation skipped // Toggle test optimizer off. prj.update_config(|config| { - config.optimize_tests = false; + config.cache_tests = false; }); // All files are rebuilt with preprocessed cache false. cmd.with_no_redact().assert_success().stdout_eq(str![[r#" @@ -36,7 +36,7 @@ Compiling 22 files with [..] // Test cache is invalidated when `forge test` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); // All files are built with optimized tests. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" @@ -55,7 +55,7 @@ No files changed, compilation skipped // Toggle test optimizer off. prj.update_config(|config| { - config.optimize_tests = false; + config.cache_tests = false; }); // All files are rebuilt with preprocessed cache false. cmd.with_no_redact().assert_success().stdout_eq(str![[r#" @@ -75,7 +75,7 @@ Compiling 20 files with [..] forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( @@ -202,7 +202,7 @@ Compiling 1 files with [..] forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( @@ -342,7 +342,7 @@ Compiling 1 files with [..] forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( @@ -499,7 +499,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( @@ -638,7 +638,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( @@ -752,7 +752,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.optimize_tests = true; + config.cache_tests = true; }); prj.add_source( From 5c22e1c7bca7d7b765591de5dd29754da85953a2 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Fri, 14 Mar 2025 11:31:45 +0200 Subject: [PATCH 04/27] Update solar and compilers revs --- Cargo.lock | 36 +++++++++++++++++++++++------------- Cargo.toml | 6 +++--- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 859d183815bca..6f9c65d9cb074 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2576,6 +2576,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.7.1" @@ -2905,6 +2914,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ + "convert_case 0.6.0", "proc-macro2", "quote", "syn 2.0.100", @@ -2917,7 +2927,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case", + "convert_case 0.7.1", "proc-macro2", "quote", "syn 2.0.100", @@ -3883,7 +3893,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" +source = "git+https://github.com/foundry-rs/compilers?rev=1a3de9f#1a3de9f9740cbdbd8853639468d6e06d5f3ce39c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3920,7 +3930,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" +source = "git+https://github.com/foundry-rs/compilers?rev=1a3de9f#1a3de9f9740cbdbd8853639468d6e06d5f3ce39c" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3929,7 +3939,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" +source = "git+https://github.com/foundry-rs/compilers?rev=1a3de9f#1a3de9f9740cbdbd8853639468d6e06d5f3ce39c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3952,7 +3962,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" +source = "git+https://github.com/foundry-rs/compilers?rev=1a3de9f#1a3de9f9740cbdbd8853639468d6e06d5f3ce39c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3966,7 +3976,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.3" -source = "git+https://github.com/foundry-rs/compilers?rev=c4ec52c#c4ec52cfe34192b2b50f90909f3bedac008de58d" +source = "git+https://github.com/foundry-rs/compilers?rev=1a3de9f#1a3de9f9740cbdbd8853639468d6e06d5f3ce39c" dependencies = [ "alloy-primitives", "cfg-if", @@ -8503,7 +8513,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "alloy-primitives", "bumpalo", @@ -8521,7 +8531,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "strum 0.26.3", ] @@ -8529,7 +8539,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "bumpalo", "index_vec", @@ -8543,7 +8553,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "annotate-snippets", "anstream", @@ -8570,7 +8580,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "proc-macro2", "quote", @@ -8580,7 +8590,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8600,7 +8610,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=964b054#964b0545260720d71ed092e8fc340be4cfd1c334" +source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 9d41db58e8683..e39dbd6486a04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -316,10 +316,10 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "c4ec52c" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "1a3de9f" } -solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } -solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "964b054" } +solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "77fb82b" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "77fb82b" } ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } From 9cdccb4c0ba57411e41c1c88dfe40e397e51dca5 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 25 Mar 2025 09:22:39 +0200 Subject: [PATCH 05/27] Update refs, add test with constructor args without name --- Cargo.lock | 24 ++++----- Cargo.toml | 6 +-- crates/forge/tests/cli/test_optimizer.rs | 65 +++++++++++++++++++++++- 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a34aabed289bb..0927ca69321ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3821,7 +3821,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=6259e8d#6259e8d91b7381624544d9746d59aef727d501ed" +source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3858,7 +3858,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=6259e8d#6259e8d91b7381624544d9746d59aef727d501ed" +source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3867,7 +3867,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=6259e8d#6259e8d91b7381624544d9746d59aef727d501ed" +source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3890,7 +3890,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=6259e8d#6259e8d91b7381624544d9746d59aef727d501ed" +source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3904,7 +3904,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=6259e8d#6259e8d91b7381624544d9746d59aef727d501ed" +source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" dependencies = [ "alloy-primitives", "cfg-if", @@ -8454,7 +8454,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "alloy-primitives", "bumpalo", @@ -8472,7 +8472,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "strum 0.26.3", ] @@ -8480,7 +8480,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "bumpalo", "index_vec", @@ -8494,7 +8494,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "annotate-snippets", "anstream", @@ -8521,7 +8521,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "proc-macro2", "quote", @@ -8531,7 +8531,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8551,7 +8551,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=77fb82b#77fb82b7ce12d9b251b49c0754a62590ae20d06b" +source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 166364de1ea4d..ba9a4434a4e70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,10 +311,10 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "6259e8d" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "ebda056" } -solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "77fb82b" } -solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "77fb82b" } +solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index cbaaac26405a2..3334ce071dcbd 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -744,6 +744,7 @@ Compiling 2 files with [..] // ├── src // │ ├── CounterA.sol +// │ ├── CounterB.sol // │ ├── Counter.sol // │ └── v1 // │ └── Counter.sol @@ -784,6 +785,24 @@ contract CounterA { owner = _owner; } + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + // Contract with constructor args without name. + prj.add_source( + "CounterB.sol", + r#" +contract CounterB { + uint256 public number; + + constructor(uint256) { + number = 1; + } + function increment() public { number++; } @@ -815,6 +834,7 @@ contract Counter { import {Test} from "forge-std/Test.sol"; import {Counter} from "src/Counter.sol"; import "src/CounterA.sol"; +import "src/CounterB.sol"; import {Counter as CounterV1} from "src/v1/Counter.sol"; contract CounterTest is Test { @@ -835,17 +855,24 @@ contract CounterTest is Test { counter.increment(); assertEq(counter.number(), 1235); } + + function test_Increment_In_Counter_B() public { + CounterB counter = new CounterB(1234); + counter.increment(); + assertEq(counter.number(), 2); + } } "#, ) .unwrap(); - // 20 files plus one mock file are compiled on first run. + // 22 files plus one mock file are compiled on first run. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... -Compiling 22 files with [..] +Compiling 23 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [PASS] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_B() (gas: [..]) [PASS] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -876,6 +903,7 @@ Compiling 1 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [PASS] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -908,6 +936,38 @@ Compiling 1 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_B() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) +... + +"#]]); + + // Change CounterB to fail test. + prj.add_source( + "CounterB.sol", + r#" +contract CounterB { + uint256 public number; + + constructor(uint256) { + number = 100; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + // Only CounterB should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[PASS] test_Increment_In_Counter() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 101 != 2] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -938,6 +998,7 @@ Compiling 1 files with [..] ... [FAIL: assertion failed: 12345 != 1] test_Increment_In_Counter() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 101 != 2] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... From 2e834fafa9879239ae9a8cba0c470e1b00ff44bb Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 25 Mar 2025 16:03:42 +0200 Subject: [PATCH 06/27] Bump compilers rev --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46ca46c0b9c36..857e06ead7c54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3868,7 +3868,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" +source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3905,7 +3905,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" +source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3914,7 +3914,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" +source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3937,7 +3937,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" +source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=ebda056#ebda056fdfd28afba65733b44575e43bcd63293a" +source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index ba9a4434a4e70..f117dde4e153b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "ebda056" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "67ac483" } solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } From fb112dd5c9fc25b2b2f9856716e6a5919ce025a4 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 25 Mar 2025 20:43:08 +0200 Subject: [PATCH 07/27] add test for named args, bump solar and compilers --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 6 +++--- crates/forge/tests/cli/test_optimizer.rs | 11 +++++++++++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 857e06ead7c54..02766d790ccfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3868,7 +3868,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" +source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3905,7 +3905,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" +source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3914,7 +3914,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" +source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3937,7 +3937,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" +source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=67ac483#67ac483d536dad7e6de298cb2a98219fe4d14fe7" +source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" dependencies = [ "alloy-primitives", "cfg-if", @@ -8439,7 +8439,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "alloy-primitives", "bumpalo", @@ -8457,7 +8457,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "strum 0.26.3", ] @@ -8465,7 +8465,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "bumpalo", "index_vec", @@ -8479,7 +8479,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "annotate-snippets", "anstream", @@ -8506,7 +8506,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "proc-macro2", "quote", @@ -8516,7 +8516,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8536,7 +8536,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?rev=63bfcb9#63bfcb9f9379f15f45955e7b97dfa722f59ae47f" +source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index f117dde4e153b..c6ecd7fd76b66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,10 +311,10 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "67ac483" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "0d0f5d9" } -solar-parse = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } -solar-sema = { git = "https://github.com/paradigmxyz/solar", rev = "63bfcb9" } +solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 3334ce071dcbd..2e209ab671523 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -856,6 +856,12 @@ contract CounterTest is Test { assertEq(counter.number(), 1235); } + function test_Increment_In_Counter_A_with_named_args() public { + CounterA counter = new CounterA({_newNumber: 1234, _owner: address(this)}); + counter.increment(); + assertEq(counter.number(), 1235); + } + function test_Increment_In_Counter_B() public { CounterB counter = new CounterB(1234); counter.increment(); @@ -872,6 +878,7 @@ Compiling 23 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [PASS] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_A_with_named_args() (gas: [..]) [PASS] test_Increment_In_Counter_B() (gas: [..]) [PASS] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -903,6 +910,7 @@ Compiling 1 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [PASS] test_Increment_In_Counter_A() (gas: [..]) +[PASS] test_Increment_In_Counter_A_with_named_args() (gas: [..]) [PASS] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -936,6 +944,7 @@ Compiling 1 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A_with_named_args() (gas: [..]) [PASS] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -967,6 +976,7 @@ Compiling 1 files with [..] ... [PASS] test_Increment_In_Counter() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A_with_named_args() (gas: [..]) [FAIL: assertion failed: 101 != 2] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... @@ -998,6 +1008,7 @@ Compiling 1 files with [..] ... [FAIL: assertion failed: 12345 != 1] test_Increment_In_Counter() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A() (gas: [..]) +[FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_A_with_named_args() (gas: [..]) [FAIL: assertion failed: 101 != 2] test_Increment_In_Counter_B() (gas: [..]) [FAIL: assertion failed: 12345 != 1235] test_Increment_In_Counter_V1() (gas: [..]) ... From 03aa00626808a8efc52d829b38541ebd2a26fe72 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Tue, 25 Mar 2025 19:58:53 +0200 Subject: [PATCH 08/27] feat(forge): add vm.deployCode cheats with msg.value and salt --- crates/cheatcodes/assets/cheatcodes.json | 120 +++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 40 ++++++++ crates/cheatcodes/src/fs.rs | 117 +++++++++++++++------- testdata/cheats/Vm.sol | 6 ++ testdata/default/cheats/DeployCode.t.sol | 80 +++++++++++++++ 5 files changed, 329 insertions(+), 34 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d8968c1118d8e..52d487c9bd9cc 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3988,6 +3988,126 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "deployCode_2", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts `msg.value`.", + "declaration": "function deployCode(string calldata artifactPath, uint256 value) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,uint256)", + "selector": "0x0af6a701", + "selectorBytes": [ + 10, + 246, + 167, + 1 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "deployCode_3", + "description": "Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts abi-encoded constructor arguments and `msg.value`.", + "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,bytes,uint256)", + "selector": "0xff5d64e4", + "selectorBytes": [ + 255, + 93, + 100, + 228 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "deployCode_4", + "description": "Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.", + "declaration": "function deployCode(string calldata artifactPath, bytes32 salt) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,bytes32)", + "selector": "0x17ab1d79", + "selectorBytes": [ + 23, + 171, + 29, + 121 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "deployCode_5", + "description": "Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts abi-encoded constructor arguments.", + "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs, bytes32 salt) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,bytes,bytes32)", + "selector": "0x016155bf", + "selectorBytes": [ + 1, + 97, + 85, + 191 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "deployCode_6", + "description": "Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts `msg.value`.", + "declaration": "function deployCode(string calldata artifactPath, uint256 value, bytes32 salt) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,uint256,bytes32)", + "selector": "0x002cb687", + "selectorBytes": [ + 0, + 44, + 182, + 135 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "deployCode_7", + "description": "Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.\nAdditionally accepts abi-encoded constructor arguments and `msg.value`.", + "declaration": "function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value, bytes32 salt) external returns (address deployedAddress);", + "visibility": "external", + "mutability": "", + "signature": "deployCode(string,bytes,uint256,bytes32)", + "selector": "0x3aa773ea", + "selectorBytes": [ + 58, + 167, + 115, + 234 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "deriveKey_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 7ee4445ab3329..af86ba40fcb10 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1873,6 +1873,46 @@ interface Vm { #[cheatcode(group = Filesystem)] function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); + /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionally accepts `msg.value`. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, uint256 value) external returns (address deployedAddress); + + /// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionally accepts abi-encoded constructor arguments and `msg.value`. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value) external returns (address deployedAddress); + + /// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, bytes32 salt) external returns (address deployedAddress); + + /// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionally accepts abi-encoded constructor arguments. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, bytes32 salt) external returns (address deployedAddress); + + /// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionally accepts `msg.value`. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, uint256 value, bytes32 salt) external returns (address deployedAddress); + + /// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. + /// + /// Additionally accepts abi-encoded constructor arguments and `msg.value`. + #[cheatcode(group = Filesystem)] + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value, bytes32 salt) external returns (address deployedAddress); + /// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file or the path to the /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index e1cb472b803a0..554b30e27cc8b 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -297,48 +297,97 @@ impl Cheatcode for getDeployedCodeCall { impl Cheatcode for deployCode_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { let Self { artifactPath: path } = self; - let bytecode = get_artifact_code(ccx.state, path, false)?; - let address = executor - .exec_create( - CreateInputs { - caller: ccx.caller, - scheme: revm::primitives::CreateScheme::Create, - value: U256::ZERO, - init_code: bytecode, - gas_limit: ccx.gas_limit, - }, - ccx, - )? - .address - .ok_or_else(|| fmt_err!("contract creation failed"))?; - - Ok(address.abi_encode()) + deploy_code(ccx, executor, path, None, None, None) } } impl Cheatcode for deployCode_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { - let Self { artifactPath: path, constructorArgs } = self; - let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); - bytecode.extend_from_slice(constructorArgs); - let address = executor - .exec_create( - CreateInputs { - caller: ccx.caller, - scheme: revm::primitives::CreateScheme::Create, - value: U256::ZERO, - init_code: bytecode.into(), - gas_limit: ccx.gas_limit, - }, - ccx, - )? - .address - .ok_or_else(|| fmt_err!("contract creation failed"))?; - - Ok(address.abi_encode()) + let Self { artifactPath: path, constructorArgs: args } = self; + deploy_code(ccx, executor, path, Some(args), None, None) + } +} + +impl Cheatcode for deployCode_2Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, value } = self; + deploy_code(ccx, executor, path, None, Some(*value), None) + } +} + +impl Cheatcode for deployCode_3Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, constructorArgs: args, value } = self; + deploy_code(ccx, executor, path, Some(args), Some(*value), None) + } +} + +impl Cheatcode for deployCode_4Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, salt } = self; + deploy_code(ccx, executor, path, None, None, Some((*salt).into())) + } +} + +impl Cheatcode for deployCode_5Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, constructorArgs: args, salt } = self; + deploy_code(ccx, executor, path, Some(args), None, Some((*salt).into())) } } +impl Cheatcode for deployCode_6Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, value, salt } = self; + deploy_code(ccx, executor, path, None, Some(*value), Some((*salt).into())) + } +} + +impl Cheatcode for deployCode_7Call { + fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + let Self { artifactPath: path, constructorArgs: args, value, salt } = self; + deploy_code(ccx, executor, path, Some(args), Some(*value), Some((*salt).into())) + } +} + +/// Helper function to deploy contract from artifact code. +/// Uses CREATE2 scheme if salt specified. +fn deploy_code( + ccx: &mut CheatsCtxt, + executor: &mut dyn CheatcodesExecutor, + path: &str, + constructor_args: Option<&Bytes>, + value: Option, + salt: Option, +) -> Result { + let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec(); + if let Some(args) = constructor_args { + bytecode.extend_from_slice(args); + } + + let scheme = if let Some(salt) = salt { + revm::primitives::CreateScheme::Create2 { salt } + } else { + revm::primitives::CreateScheme::Create + }; + + let address = executor + .exec_create( + CreateInputs { + caller: ccx.caller, + scheme, + value: value.unwrap_or(U256::ZERO), + init_code: bytecode.into(), + gas_limit: ccx.gas_limit, + }, + ccx, + )? + .address + .ok_or_else(|| fmt_err!("contract creation failed"))?; + + Ok(address.abi_encode()) +} + /// Returns the path to the json artifact depending on the input /// /// Can parse following input formats: diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 38e4cae39bc07..86366241a7fce 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -193,6 +193,12 @@ interface Vm { function deleteStateSnapshots() external; function deployCode(string calldata artifactPath) external returns (address deployedAddress); function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, uint256 value) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, bytes32 salt) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, bytes32 salt) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, uint256 value, bytes32 salt) external returns (address deployedAddress); + function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value, bytes32 salt) external returns (address deployedAddress); function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) external pure returns (uint256 privateKey); function deriveKey(string calldata mnemonic, uint32 index, string calldata language) external pure returns (uint256 privateKey); diff --git a/testdata/default/cheats/DeployCode.t.sol b/testdata/default/cheats/DeployCode.t.sol index edf4c78e6dab4..3978cfc335f5f 100644 --- a/testdata/default/cheats/DeployCode.t.sol +++ b/testdata/default/cheats/DeployCode.t.sol @@ -16,6 +16,26 @@ contract TestContractWithArgs { } } +contract TestPayableContract { + uint256 public a; + + constructor() payable { + a = msg.value; + } +} + +contract TestPayableContractWithArgs { + uint256 public a; + uint256 public b; + uint256 public c; + + constructor(uint256 _a, uint256 _b) payable { + a = _a; + b = _b; + c = msg.value; + } +} + contract DeployCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -39,4 +59,64 @@ contract DeployCodeTest is DSTest { assertEq(withDeployCode.a(), 3); assertEq(withDeployCode.b(), 4); } + + function testDeployCodeWithPayableConstructorAndArgs() public { + address withNew = address(new TestPayableContractWithArgs(1, 2)); + TestPayableContractWithArgs withDeployCode = TestPayableContractWithArgs( + vm.deployCode("cheats/DeployCode.t.sol:TestPayableContractWithArgs", abi.encode(3, 4), 101) + ); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 3); + assertEq(withDeployCode.b(), 4); + assertEq(withDeployCode.c(), 101); + } + + function testDeployCodeWithPayableConstructor() public { + address withNew = address(new TestPayableContract()); + TestPayableContract withDeployCode = + TestPayableContract(vm.deployCode("cheats/DeployCode.t.sol:TestPayableContract", 111)); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 111); + } + + function testDeployCodeWithSalt() public { + address addrDefault = address(new TestContract()); + address addrDeployCode = vm.deployCode("cheats/DeployCode.t.sol:TestContract", bytes32("salt")); + + assertEq(addrDefault.code, addrDeployCode.code); + } + + function testDeployCodeWithArgsAndSalt() public { + address withNew = address(new TestContractWithArgs(1, 2)); + TestContractWithArgs withDeployCode = TestContractWithArgs( + vm.deployCode("cheats/DeployCode.t.sol:TestContractWithArgs", abi.encode(3, 4), bytes32("salt")) + ); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 3); + assertEq(withDeployCode.b(), 4); + } + + function testDeployCodeWithPayableConstructorAndSalt() public { + address withNew = address(new TestPayableContract()); + TestPayableContract withDeployCode = + TestPayableContract(vm.deployCode("cheats/DeployCode.t.sol:TestPayableContract", 111, bytes32("salt"))); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 111); + } + + function testDeployCodeWithPayableConstructorAndArgsAndSalt() public { + address withNew = address(new TestPayableContractWithArgs(1, 2)); + TestPayableContractWithArgs withDeployCode = TestPayableContractWithArgs( + vm.deployCode("cheats/DeployCode.t.sol:TestPayableContractWithArgs", abi.encode(3, 4), 101, bytes32("salt")) + ); + + assertEq(withNew.code, address(withDeployCode).code); + assertEq(withDeployCode.a(), 3); + assertEq(withDeployCode.b(), 4); + assertEq(withDeployCode.c(), 101); + } } From 4c6939656a6ae46b5c689db8479fe85f7704e352 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 26 Mar 2025 15:06:39 +0200 Subject: [PATCH 09/27] Bump compilers, ctor test with value and salt, ignore win panic --- Cargo.lock | 10 +- Cargo.toml | 2 +- crates/forge/tests/cli/test_optimizer.rs | 218 +++++++++++++++++++---- 3 files changed, 194 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02766d790ccfe..7e8ab5f0985d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3868,7 +3868,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" +source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3905,7 +3905,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" +source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3914,7 +3914,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" +source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3937,7 +3937,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" +source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=0d0f5d9#0d0f5d9093ee008f70c02a1527b8fc81983fa053" +source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index c6ecd7fd76b66..9860e9c8bc1c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "0d0f5d9" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "322e80a" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 2e209ab671523..d5ba581712994 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1,70 +1,78 @@ //! Tests for the `forge test` with preprocessed cache. // Test cache is invalidated when `forge build` if optimize test option toggled. -forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { - prj.update_config(|config| { - config.cache_tests = true; - }); - // All files are built with optimized tests. - cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" +forgetest_init!( + #[cfg_attr(windows, ignore = "TODO: fix compilers panic")] + toggle_invalidate_cache_on_build, + |prj, cmd| { + prj.update_config(|config| { + config.cache_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 22 files with [..] ... "#]]); - // No files are rebuilt. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... No files changed, compilation skipped ... "#]]); - // Toggle test optimizer off. - prj.update_config(|config| { - config.cache_tests = false; - }); - // All files are rebuilt with preprocessed cache false. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // Toggle test optimizer off. + prj.update_config(|config| { + config.cache_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 22 files with [..] ... "#]]); -}); + } +); // Test cache is invalidated when `forge test` if optimize test option toggled. -forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { - prj.update_config(|config| { - config.cache_tests = true; - }); - // All files are built with optimized tests. - cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +forgetest_init!( + #[cfg_attr(windows, ignore = "TODO: fix compilers panic")] + toggle_invalidate_cache_on_test, + |prj, cmd| { + prj.update_config(|config| { + config.cache_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 20 files with [..] ... "#]]); - // No files are rebuilt. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... No files changed, compilation skipped ... "#]]); - // Toggle test optimizer off. - prj.update_config(|config| { - config.cache_tests = false; - }); - // All files are rebuilt with preprocessed cache false. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // Toggle test optimizer off. + prj.update_config(|config| { + config.cache_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 20 files with [..] ... "#]]); -}); + } +); // Counter contract without interface instantiated in CounterTest // @@ -1015,3 +1023,153 @@ Compiling 1 files with [..] "#]]); }); + +// Test preprocessing contracts with payable constructor, value and salt named args. +forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.cache_tests = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + constructor(uint256 _number) payable { + number = msg.value; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + prj.add_source( + "CounterWithSalt.sol", + r#" +contract CounterWithSalt { + uint256 public number; + + constructor(uint256 _number) payable { + number = msg.value; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "src/Counter.sol"; +import {CounterWithSalt} from "src/CounterWithSalt.sol"; + +contract CounterTest is Test { + function test_Increment_In_Counter() public { + Counter counter = new Counter{value: 111}(1); + counter.increment(); + assertEq(counter.number(), 112); + } + + function test_Increment_In_Counter_With_Salt() public { + CounterWithSalt counter = new CounterWithSalt{value: 111, salt: bytes32("preprocess_counter_with_salt")}(1); + assertEq(address(counter), 0x3Efe9ecFc73fB3baB7ECafBB40D3e134260Be6AB); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 21 files with [..] +... +[PASS] test_Increment_In_Counter() (gas: [..]) +[PASS] test_Increment_In_Counter_With_Salt() (gas: [..]) +... + +"#]]); + + // Change contract to fail test. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + constructor(uint256 _number) payable { + number = msg.value + _number; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + // Only Counter should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 113 != 112] test_Increment_In_Counter() (gas: [..]) +[PASS] test_Increment_In_Counter_With_Salt() (gas: [..]) +... + +"#]]); + + // Change contract with salt to fail test too. + prj.add_source( + "CounterWithSalt.sol", + r#" +contract CounterWithSalt { + uint256 public number; + + constructor(uint256 _number) payable { + number = msg.value + _number; + } + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} + "#, + ) + .unwrap(); + // Only Counter should be compiled and test should fail. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: assertion failed: 113 != 112] test_Increment_In_Counter() (gas: [..]) +[FAIL: assertion failed: 0x6cDcb015cFcAd0C23560322EdEE8f324520E4b93 != 0x3Efe9ecFc73fB3baB7ECafBB40D3e134260Be6AB] test_Increment_In_Counter_With_Salt() (gas: [..]) +... + +"#]]); +}); From 863435e5521f0d0a09fb0f66a51dd486d0ae837e Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 27 Mar 2025 07:24:33 +0200 Subject: [PATCH 10/27] Bump compilers with fix for win panic --- Cargo.lock | 10 ++-- Cargo.toml | 2 +- crates/forge/tests/cli/test_optimizer.rs | 68 +++++++++++------------- 3 files changed, 36 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e8ab5f0985d8..86d5561088dac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3868,7 +3868,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" +source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3905,7 +3905,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" +source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3914,7 +3914,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" +source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3937,7 +3937,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" +source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=322e80a#322e80ac30b02e3a05cde1201dcc4d0b9acc10e7" +source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 9860e9c8bc1c7..c44f3b8d8f5cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "322e80a" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "446a5d8" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index d5ba581712994..963266003a013 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1,78 +1,70 @@ //! Tests for the `forge test` with preprocessed cache. // Test cache is invalidated when `forge build` if optimize test option toggled. -forgetest_init!( - #[cfg_attr(windows, ignore = "TODO: fix compilers panic")] - toggle_invalidate_cache_on_build, - |prj, cmd| { - prj.update_config(|config| { - config.cache_tests = true; - }); - // All files are built with optimized tests. - cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" +forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { + prj.update_config(|config| { + config.cache_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 22 files with [..] ... "#]]); - // No files are rebuilt. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... No files changed, compilation skipped ... "#]]); - // Toggle test optimizer off. - prj.update_config(|config| { - config.cache_tests = false; - }); - // All files are rebuilt with preprocessed cache false. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // Toggle test optimizer off. + prj.update_config(|config| { + config.cache_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 22 files with [..] ... "#]]); - } -); +}); // Test cache is invalidated when `forge test` if optimize test option toggled. -forgetest_init!( - #[cfg_attr(windows, ignore = "TODO: fix compilers panic")] - toggle_invalidate_cache_on_test, - |prj, cmd| { - prj.update_config(|config| { - config.cache_tests = true; - }); - // All files are built with optimized tests. - cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { + prj.update_config(|config| { + config.cache_tests = true; + }); + // All files are built with optimized tests. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 20 files with [..] ... "#]]); - // No files are rebuilt. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // No files are rebuilt. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... No files changed, compilation skipped ... "#]]); - // Toggle test optimizer off. - prj.update_config(|config| { - config.cache_tests = false; - }); - // All files are rebuilt with preprocessed cache false. - cmd.with_no_redact().assert_success().stdout_eq(str![[r#" + // Toggle test optimizer off. + prj.update_config(|config| { + config.cache_tests = false; + }); + // All files are rebuilt with preprocessed cache false. + cmd.with_no_redact().assert_success().stdout_eq(str![[r#" ... Compiling 20 files with [..] ... "#]]); - } -); +}); // Counter contract without interface instantiated in CounterTest // From 619f10a87303377677b483c499ee1825175c1a8d Mon Sep 17 00:00:00 2001 From: grandizzy Date: Fri, 28 Mar 2025 09:09:34 +0200 Subject: [PATCH 11/27] Bump compilers --- Cargo.lock | 46 +++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86d5561088dac..bcc14ccfa1d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2592,15 +2592,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.7.1" @@ -2930,7 +2921,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case 0.6.0", "proc-macro2", "quote", "syn 2.0.100", @@ -2943,7 +2933,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case 0.7.1", + "convert_case", "proc-macro2", "quote", "syn 2.0.100", @@ -3868,7 +3858,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" +source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3905,7 +3895,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" +source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3914,7 +3904,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" +source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3937,7 +3927,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" +source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,7 +3941,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=446a5d8#446a5d817c83f8b2ff27d06cafe4ffdfb5db686a" +source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" dependencies = [ "alloy-primitives", "cfg-if", @@ -8439,10 +8429,11 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "alloy-primitives", "bumpalo", + "derive_more 2.0.1", "either", "num-bigint", "num-rational", @@ -8450,22 +8441,22 @@ dependencies = [ "solar-data-structures", "solar-interface", "solar-macros", - "strum 0.26.3", + "strum 0.27.1", "typed-arena", ] [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ - "strum 0.26.3", + "strum 0.27.1", ] [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "bumpalo", "index_vec", @@ -8479,13 +8470,14 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "annotate-snippets", "anstream", "anstyle", "const-hex", "derive_builder", + "derive_more 2.0.1", "dunce", "itertools 0.10.5", "itoa", @@ -8506,7 +8498,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "proc-macro2", "quote", @@ -8516,7 +8508,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8536,13 +8528,13 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#71b3445065e1b5e48a9f5ea6cbb6964643869084" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" dependencies = [ "alloy-json-abi", "alloy-primitives", "bitflags 2.9.0", "bumpalo", - "derive_more 1.0.0", + "derive_more 2.0.1", "either", "once_map", "paste", @@ -8555,7 +8547,7 @@ dependencies = [ "solar-interface", "solar-macros", "solar-parse", - "strum 0.26.3", + "strum 0.27.1", "thread_local", "tracing", "typed-arena", diff --git a/Cargo.toml b/Cargo.toml index c44f3b8d8f5cb..3d85d65c8d1b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "446a5d8" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "d27f6f4" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } From 3f857687ad6a259a0b50fdb8ce7f862798cf76de Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:14:12 +0300 Subject: [PATCH 12/27] Update crates/common/src/compile.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/common/src/compile.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index b425c2403460e..635825babec50 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -174,13 +174,12 @@ impl ProjectCompiler { project.paths.read_input_files()? }; - let compiler = + let mut compiler = foundry_compilers::project::ProjectCompiler::with_sources(project, sources)?; if preprocess { - compiler.with_preprocessor(TestOptimizerPreprocessor).compile().map_err(Into::into) - } else { - compiler.compile().map_err(Into::into) + compiler = compiler.with_preprocessor(TestOptimizerPreprocessor); } + compiler.compile().map_err(Into::into) }) } From 797133d45314fc4a5f5ef85d25508f3b33c4f0f9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:43:23 +0200 Subject: [PATCH 13/27] bump --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 2 +- crates/config/src/lib.rs | 3 +-- crates/verify/src/provider.rs | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42a2b7380e778..67e4ade6bb1b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3873,7 +3873,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" +source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3886,7 +3886,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.13.0", + "itertools 0.14.0", "md-5", "path-slash", "rand 0.8.5", @@ -3910,7 +3910,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" +source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3919,7 +3919,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" +source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3942,7 +3942,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" +source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3956,7 +3956,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=d27f6f4#d27f6f4d48bc1970ca0ba951c89aa08e1e29d70a" +source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" dependencies = [ "alloy-primitives", "cfg-if", @@ -8446,7 +8446,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "alloy-primitives", "bumpalo", @@ -8465,7 +8465,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "strum 0.27.1", ] @@ -8473,7 +8473,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "bumpalo", "index_vec", @@ -8487,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "annotate-snippets", "anstream", @@ -8496,7 +8496,7 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.10.5", + "itertools 0.14.0", "itoa", "lasso", "match_cfg", @@ -8507,7 +8507,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "tracing", "unicode-width 0.2.0", ] @@ -8515,7 +8515,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "proc-macro2", "quote", @@ -8525,12 +8525,12 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "alloy-primitives", "bitflags 2.9.0", "bumpalo", - "itertools 0.10.5", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ebe7b15ba18c5f6e9f5bf1bba9f50414c3dc4a87" +source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 3d85d65c8d1b2..0fc533c0329b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "d27f6f4" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "a48b275" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8b7641b244670..8d4cf5f77cc90 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1194,7 +1194,7 @@ impl Config { /// Returns configured [Vyper] compiler. pub fn vyper_compiler(&self) -> Result, SolcError> { // Only instantiate Vyper if there are any Vyper files in the project. - if self.project_paths::().input_files_iter().next().is_none() { + if !self.project_paths::().has_input_files() { return Ok(None); } let vyper = if let Some(path) = &self.vyper.path { @@ -1202,7 +1202,6 @@ impl Config { } else { Vyper::new("vyper").ok() }; - Ok(vyper) } diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 01a06332a36ce..74d2094c41b83 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -91,7 +91,7 @@ impl VerificationContext { let graph = Graph::::resolve_sources(&self.project.paths, sources)?; - Ok(graph.imports(&self.target_path).into_iter().cloned().collect()) + Ok(graph.imports(&self.target_path).into_iter().map(Into::into).collect()) } } From bd2b8cabff22a1baa04a94ee92965ef2ce1a7e7a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:10:19 +0200 Subject: [PATCH 14/27] bump solar --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67e4ade6bb1b3..adc76c0ec0e6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8446,7 +8446,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "alloy-primitives", "bumpalo", @@ -8465,7 +8465,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "strum 0.27.1", ] @@ -8473,7 +8473,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "bumpalo", "index_vec", @@ -8487,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "annotate-snippets", "anstream", @@ -8515,7 +8515,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "proc-macro2", "quote", @@ -8525,7 +8525,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#02dcecd0ec81bce4e306e6d1ed0a48e9e4f3de8a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" dependencies = [ "alloy-json-abi", "alloy-primitives", From 3f73080484c9c687f1b29ef6965648f361cebcc4 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:13:59 +0200 Subject: [PATCH 15/27] bump solar 2 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adc76c0ec0e6c..76244bb076183 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8446,7 +8446,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "alloy-primitives", "bumpalo", @@ -8465,7 +8465,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "strum 0.27.1", ] @@ -8473,7 +8473,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "bumpalo", "index_vec", @@ -8487,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "annotate-snippets", "anstream", @@ -8515,7 +8515,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "proc-macro2", "quote", @@ -8525,7 +8525,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#44cac07d90c3c395be7e53909842535798f77236" +source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" dependencies = [ "alloy-json-abi", "alloy-primitives", From c3dd142313fe6efcaea1e60c5a03cefc4663f0f5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:37:42 +0200 Subject: [PATCH 16/27] bump compilers --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0fc533c0329b2..a46e556d66368 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "a48b275" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "49b65bf" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } From 0cc772cf4d71dc902900c0bb6423fa0ba007b9ca Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 3 Apr 2025 14:06:30 +0300 Subject: [PATCH 17/27] ename config dynamic_test_linking, bump compilers --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- crates/cli/src/opts/build/core.rs | 8 ++++---- crates/common/src/compile.rs | 16 ++++++++-------- crates/config/src/lib.rs | 6 +++--- crates/forge/src/cmd/build.rs | 2 +- crates/forge/src/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/config.rs | 6 +++--- crates/forge/tests/cli/test_optimizer.rs | 22 +++++++++++----------- crates/script/src/build.rs | 2 +- 10 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76244bb076183..a24b047415313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3873,7 +3873,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" +source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3886,7 +3886,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.14.0", + "itertools 0.13.0", "md-5", "path-slash", "rand 0.8.5", @@ -3910,7 +3910,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" +source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3919,7 +3919,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" +source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3942,7 +3942,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" +source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3956,7 +3956,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=a48b275#a48b275a404767708ebca58ddb5f4820974feb68" +source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index a46e556d66368..fe17db5caefcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,7 +311,7 @@ vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "49b65bf" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "93b3c3f" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index e1df9b96ad141..a1b0f384bfae4 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -34,10 +34,10 @@ pub struct BuildOpts { #[serde(skip)] pub no_cache: bool, - /// Enable preprocessed cache for tests. + /// Enable preprocessed cache for tests #[arg(long, conflicts_with = "no_cache")] #[serde(skip)] - pub cache_tests: bool, + pub dynamic_test_linking: bool, /// Set pre-linked libraries. #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] @@ -250,8 +250,8 @@ impl Provider for BuildOpts { dict.insert("cache".to_string(), false.into()); } - if self.cache_tests { - dict.insert("cache_tests".to_string(), true.into()); + if self.dynamic_test_linking || self.no_cache { + dict.insert("dynamic_test_linking".to_string(), true.into()); } if self.build_info { diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 635825babec50..4c19e57a78082 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -62,8 +62,8 @@ pub struct ProjectCompiler { /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, - /// Whether to compile with preprocessed cache for tests and scripts. - cache_tests: bool, + /// Whether to compile with dynamic linking tests and scripts. + dynamic_test_linking: bool, } impl Default for ProjectCompiler { @@ -86,7 +86,7 @@ impl ProjectCompiler { bail: None, ignore_eip_3860: false, files: Vec::new(), - cache_tests: false, + dynamic_test_linking: false, } } @@ -140,10 +140,10 @@ impl ProjectCompiler { self } - /// Sets if compiler should use preprocessed cache. + /// Sets if tests should be dynamically linked. #[inline] - pub fn cache_tests(mut self, preprocess: bool) -> Self { - self.cache_tests = preprocess; + pub fn dynamic_test_linking(mut self, preprocess: bool) -> Self { + self.dynamic_test_linking = preprocess; self } @@ -155,7 +155,7 @@ impl ProjectCompiler { where TestOptimizerPreprocessor: Preprocessor, { - self.project_root = project.root().clone(); + self.project_root = project.root().to_path_buf(); // TODO: Avoid process::exit if !project.paths.has_input_files() && self.files.is_empty() { @@ -166,7 +166,7 @@ impl ProjectCompiler { // Taking is fine since we don't need these in `compile_with`. let files = std::mem::take(&mut self.files); - let preprocess = self.cache_tests; + let preprocess = self.dynamic_test_linking; self.compile_with(|| { let sources = if !files.is_empty() { Source::read_all(files)? diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8d4cf5f77cc90..681917cbab9d4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -194,8 +194,8 @@ pub struct Config { pub libraries: Vec, /// whether to enable cache pub cache: bool, - /// whether to enable preprocessed cache for tests - pub cache_tests: bool, + /// whether to dynamically link tests + pub dynamic_test_linking: bool, /// where the cache is stored if enabled pub cache_path: PathBuf, /// where the gas snapshots are stored @@ -2281,7 +2281,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, - cache_tests: false, + dynamic_test_linking: false, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index e970afd6a5fa0..e64569206775b 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -92,7 +92,7 @@ impl BuildArgs { let format_json = shell::is_json(); let compiler = ProjectCompiler::new() .files(files) - .cache_tests(config.cache_tests) + .dynamic_test_linking(config.dynamic_test_linking) .print_names(self.names) .print_sizes(self.sizes) .ignore_eip_3860(self.ignore_eip_3860) diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index 5d5ce5cb14d9f..76ac610ccef45 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -306,7 +306,7 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; let compiler = ProjectCompiler::new() - .cache_tests(config.cache_tests) + .dynamic_test_linking(config.dynamic_test_linking) .quiet(shell::is_json() || self.junit) .files(sources_to_compile); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index b49803753a4c0..5308a2918a6aa 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,7 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, - cache_tests: false, + dynamic_test_linking: false, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -972,7 +972,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true -cache_tests = false +dynamic_test_linking = false cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1130,7 +1130,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, - "cache_tests": false, + "dynamic_test_linking": false, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 963266003a013..ed606e8353d04 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -3,7 +3,7 @@ // Test cache is invalidated when `forge build` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); // All files are built with optimized tests. cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" @@ -22,7 +22,7 @@ No files changed, compilation skipped // Toggle test optimizer off. prj.update_config(|config| { - config.cache_tests = false; + config.dynamic_test_linking = false; }); // All files are rebuilt with preprocessed cache false. cmd.with_no_redact().assert_success().stdout_eq(str![[r#" @@ -36,7 +36,7 @@ Compiling 22 files with [..] // Test cache is invalidated when `forge test` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); // All files are built with optimized tests. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" @@ -55,7 +55,7 @@ No files changed, compilation skipped // Toggle test optimizer off. prj.update_config(|config| { - config.cache_tests = false; + config.dynamic_test_linking = false; }); // All files are rebuilt with preprocessed cache false. cmd.with_no_redact().assert_success().stdout_eq(str![[r#" @@ -75,7 +75,7 @@ Compiling 20 files with [..] forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -202,7 +202,7 @@ Compiling 1 files with [..] forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -342,7 +342,7 @@ Compiling 1 files with [..] forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -499,7 +499,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -638,7 +638,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -753,7 +753,7 @@ Compiling 2 files with [..] forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( @@ -1020,7 +1020,7 @@ Compiling 1 files with [..] forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { prj.wipe_contracts(); prj.update_config(|config| { - config.cache_tests = true; + config.dynamic_test_linking = true; }); prj.add_source( diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index d3802d60c38bc..885e7ca5879be 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -229,7 +229,7 @@ impl PreprocessedState { args, script_config, script_wallets, - build_data: BuildData { output, target, project_root: project.root().clone() }, + build_data: BuildData { output, target, project_root: project.root().to_path_buf() }, }) } } From b1fc2a564fe8f87075bab0fcba1546d0509a0503 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 3 Apr 2025 17:14:26 +0300 Subject: [PATCH 18/27] Dynamic test linking default enabled --- crates/cli/src/opts/build/core.rs | 10 ++++----- crates/config/src/lib.rs | 2 +- crates/forge/tests/cli/cmd.rs | 1 + crates/forge/tests/cli/config.rs | 6 +++--- crates/forge/tests/cli/test_cmd.rs | 9 ++++++++ crates/forge/tests/cli/test_optimizer.rs | 27 ------------------------ 6 files changed, 19 insertions(+), 36 deletions(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index a1b0f384bfae4..8f35d918de089 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -34,10 +34,10 @@ pub struct BuildOpts { #[serde(skip)] pub no_cache: bool, - /// Enable preprocessed cache for tests - #[arg(long, conflicts_with = "no_cache")] + /// Disable dynamic test linking. + #[arg(long)] #[serde(skip)] - pub dynamic_test_linking: bool, + pub no_dynamic_test_linking: bool, /// Set pre-linked libraries. #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] @@ -250,8 +250,8 @@ impl Provider for BuildOpts { dict.insert("cache".to_string(), false.into()); } - if self.dynamic_test_linking || self.no_cache { - dict.insert("dynamic_test_linking".to_string(), true.into()); + if self.no_dynamic_test_linking || self.no_cache { + dict.insert("dynamic_test_linking".to_string(), false.into()); } if self.build_info { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 681917cbab9d4..c855193c86012 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2281,7 +2281,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, - dynamic_test_linking: false, + dynamic_test_linking: true, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index f845981bda592..274784101bc8b 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -3602,6 +3602,7 @@ forgetest!(inspect_custom_counter_method_identifiers, |prj, cmd| { // checks that `clean` also works with the "out" value set in Config forgetest_init!(gas_report_include_tests, |prj, cmd| { prj.update_config(|config| { + config.dynamic_test_linking = false; config.gas_reports_include_tests = true; config.fuzz.runs = 1; }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 5308a2918a6aa..755e89fb583da 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,7 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, - dynamic_test_linking: false, + dynamic_test_linking: true, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -972,7 +972,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true -dynamic_test_linking = false +dynamic_test_linking = true cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1130,7 +1130,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, - "dynamic_test_linking": false, + "dynamic_test_linking": true, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index a13f8c51ba81a..a02c268623c9e 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2337,6 +2337,9 @@ Logs: // forgetest_init!(metadata_bytecode_traces, |prj, cmd| { + prj.update_config(|config| { + config.dynamic_test_linking = false; + }); prj.add_source( "ParentProxy.sol", r#" @@ -2895,6 +2898,9 @@ Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] // Tests that test traces display state changes when running with verbosity. #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(should_show_state_changes, |prj, cmd| { + prj.update_config(|config| { + config.dynamic_test_linking = false; + }); cmd.args(["test", "--mt", "test_Increment", "-vvvvv"]).assert_success().stdout_eq(str![[r#" ... Ran 1 test for test/Counter.t.sol:CounterTest @@ -3003,6 +3009,9 @@ forgetest_init!(colored_traces, |prj, cmd| { // #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(should_only_show_failed_tests_trace, |prj, cmd| { + prj.update_config(|config| { + config.dynamic_test_linking = false; + }); prj.add_test( "SuppressTracesTest.t.sol", r#" diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index ed606e8353d04..c81f06f49c5bf 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -2,9 +2,6 @@ // Test cache is invalidated when `forge build` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); // All files are built with optimized tests. cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -35,9 +32,6 @@ Compiling 22 files with [..] // Test cache is invalidated when `forge test` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); // All files are built with optimized tests. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -74,9 +68,6 @@ Compiling 20 files with [..] // └── Counter.t.sol forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", @@ -201,9 +192,6 @@ Compiling 1 files with [..] // └── Counter.t.sol forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "interface/CounterIf.sol", @@ -341,9 +329,6 @@ Compiling 1 files with [..] // └── CounterMock.sol forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", @@ -498,9 +483,6 @@ Compiling 2 files with [..] // └── CounterMock.sol forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", @@ -637,9 +619,6 @@ Compiling 2 files with [..] // └── CounterMock.sol forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", @@ -752,9 +731,6 @@ Compiling 2 files with [..] // └── Counter.t.sol forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", @@ -1019,9 +995,6 @@ Compiling 1 files with [..] // Test preprocessing contracts with payable constructor, value and salt named args. forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { prj.wipe_contracts(); - prj.update_config(|config| { - config.dynamic_test_linking = true; - }); prj.add_source( "Counter.sol", From ab58a04877e15102da12b4ae03e8eeacf45bd858 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 3 Apr 2025 18:14:17 +0300 Subject: [PATCH 19/27] More test fixes --- crates/forge/tests/cli/test_cmd.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index a02c268623c9e..76c299a329e0b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -3000,6 +3000,9 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(colored_traces, |prj, cmd| { + prj.update_config(|config| { + config.dynamic_test_linking = false; + }); cmd.args(["test", "--mt", "test_Increment", "--color", "always", "-vvvvv"]) .assert_success() .stdout_eq(file!["../fixtures/colored_traces.svg": TermSvg]); From d7ab9a1bd413f89372bbac46d8d073bd97407b60 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:13:09 +0200 Subject: [PATCH 20/27] chore: move preprocessor from compilers --- Cargo.lock | 15 +- Cargo.toml | 11 +- crates/common/Cargo.toml | 4 + crates/common/src/compile.rs | 2 +- crates/common/src/lib.rs | 1 + crates/common/src/preprocessor/data.rs | 200 +++++++++++++ crates/common/src/preprocessor/deps.rs | 370 +++++++++++++++++++++++++ crates/common/src/preprocessor/mod.rs | 157 +++++++++++ 8 files changed, 750 insertions(+), 10 deletions(-) create mode 100644 crates/common/src/preprocessor/data.rs create mode 100644 crates/common/src/preprocessor/deps.rs create mode 100644 crates/common/src/preprocessor/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a24b047415313..16c5c68e027f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3835,10 +3835,13 @@ dependencies = [ "foundry-config", "itertools 0.14.0", "num-format", + "path-slash", "reqwest", "semver 1.0.26", "serde", "serde_json", + "solar-parse", + "solar-sema", "terminal_size", "thiserror 2.0.12", "tokio", @@ -3873,7 +3876,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" +source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3886,7 +3889,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.13.0", + "itertools 0.14.0", "md-5", "path-slash", "rand 0.8.5", @@ -3910,7 +3913,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" +source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3919,7 +3922,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" +source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3942,7 +3945,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" +source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3956,7 +3959,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=93b3c3f#93b3c3f5b79e8fca45be3e786a2a9e8a25d6fb59" +source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index fe17db5caefcd..e60aa2bbdde9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,6 +193,7 @@ foundry-compilers = { version = "0.13.5", default-features = false } foundry-fork-db = "0.12" solang-parser = "=0.3.3" solar-parse = { version = "=0.1.1", default-features = false } +solar-sema = { version = "=0.1.1", default-features = false } ## revm revm = { version = "19.4.0", default-features = false } @@ -309,12 +310,16 @@ tracing-subscriber = "0.3" url = "2" vergen = { version = "8", default-features = false } yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } +path-slash = "0.2" [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "93b3c3f" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "5377589" } +# foundry-compilers = { path = "../compilers/crates/compilers" } -solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } -solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +# solar-parse = { path = "../../paradigmxyz/solar/crates/parse" } +# solar-sema = { path = "../../paradigmxyz/solar/crates/sema" } ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 314a2c5d0388b..11e67174b65be 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -45,6 +45,9 @@ alloy-transport.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } alloy-network.workspace = true +solar-parse.workspace = true +solar-sema.workspace = true + tower.workspace = true async-trait.workspace = true @@ -64,6 +67,7 @@ tracing.workspace = true url.workspace = true walkdir.workspace = true yansi.workspace = true +path-slash.workspace = true anstream.workspace = true anstyle.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 4c19e57a78082..ecae238cabb1d 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -1,6 +1,7 @@ //! Support for compiling [foundry_compilers::Project] use crate::{ + preprocessor::TestOptimizerPreprocessor, reports::{report_kind, ReportKind}, shell, term::SpinnerReporter, @@ -16,7 +17,6 @@ use foundry_compilers::{ Compiler, }, info::ContractInfo as CompilerContractInfo, - preprocessor::TestOptimizerPreprocessor, project::Preprocessor, report::{BasicStdoutReporter, NoReporter, Report}, solc::SolcSettings, diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 3779492935fcf..5981d317ab1d9 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -25,6 +25,7 @@ pub mod ens; pub mod errors; pub mod evm; pub mod fs; +mod preprocessor; pub mod provider; pub mod reports; pub mod retry; diff --git a/crates/common/src/preprocessor/data.rs b/crates/common/src/preprocessor/data.rs new file mode 100644 index 0000000000000..f7a31686e030a --- /dev/null +++ b/crates/common/src/preprocessor/data.rs @@ -0,0 +1,200 @@ +use super::span_to_range; +use foundry_compilers::artifacts::{Source, Sources}; +use path_slash::PathExt; +use solar_parse::interface::{Session, SourceMap}; +use solar_sema::{ + hir::{Contract, ContractId, Hir}, + interface::source_map::FileName, +}; +use std::{ + collections::{BTreeMap, HashSet}, + path::{Path, PathBuf}, +}; + +/// Keeps data about project contracts definitions referenced from tests and scripts. +/// Contract id -> Contract data definition mapping. +pub type PreprocessorData = BTreeMap; + +/// Collects preprocessor data from referenced contracts. +pub(crate) fn collect_preprocessor_data( + sess: &Session, + hir: &Hir<'_>, + referenced_contracts: &HashSet, +) -> PreprocessorData { + let mut data = PreprocessorData::default(); + for contract_id in referenced_contracts { + let contract = hir.contract(*contract_id); + let source = hir.source(contract.source); + + let FileName::Real(path) = &source.file.name else { + continue; + }; + + let contract_data = + ContractData::new(hir, *contract_id, contract, path, source, sess.source_map()); + data.insert(*contract_id, contract_data); + } + data +} + +/// Creates helper libraries for contracts with a non-empty constructor. +/// +/// See [`ContractData::build_helper`] for more details. +pub(crate) fn create_deploy_helpers(data: &BTreeMap) -> Sources { + let mut deploy_helpers = Sources::new(); + for (contract_id, contract) in data { + if let Some(code) = contract.build_helper() { + let path = format!("foundry-pp/DeployHelper{}.sol", contract_id.get()); + deploy_helpers.insert(path.into(), Source::new(code)); + } + } + deploy_helpers +} + +/// Keeps data about a contract constructor. +#[derive(Debug)] +pub struct ContractConstructorData { + /// ABI encoded args. + pub abi_encode_args: String, + /// Constructor struct fields. + pub struct_fields: String, +} + +/// Keeps data about a single contract definition. +#[derive(Debug)] +pub(crate) struct ContractData { + /// HIR Id of the contract. + contract_id: ContractId, + /// Path of the source file. + path: PathBuf, + /// Name of the contract + name: String, + /// Constructor parameters, if any. + pub constructor_data: Option, + /// Artifact string to pass into cheatcodes. + pub artifact: String, +} + +impl ContractData { + fn new( + hir: &Hir<'_>, + contract_id: ContractId, + contract: &Contract<'_>, + path: &Path, + source: &solar_sema::hir::Source<'_>, + source_map: &SourceMap, + ) -> Self { + let artifact = format!("{}:{}", path.to_slash_lossy(), contract.name); + + // Process data for contracts with constructor and parameters. + let constructor_data = contract + .ctor + .map(|ctor_id| hir.function(ctor_id)) + .filter(|ctor| !ctor.parameters.is_empty()) + .map(|ctor| { + let mut abi_encode_args = vec![]; + let mut struct_fields = vec![]; + let mut arg_index = 0; + for param_id in ctor.parameters { + let src = source.file.src.as_str(); + let loc = span_to_range(source_map, hir.variable(*param_id).span); + let mut new_src = src[loc].replace(" memory ", " ").replace(" calldata ", " "); + if let Some(ident) = hir.variable(*param_id).name { + abi_encode_args.push(format!("args.{}", ident.name)); + } else { + // Generate an unique name if constructor arg doesn't have one. + arg_index += 1; + abi_encode_args.push(format!("args.foundry_pp_ctor_arg{arg_index}")); + new_src.push_str(&format!(" foundry_pp_ctor_arg{arg_index}")); + } + struct_fields.push(new_src); + } + + ContractConstructorData { + abi_encode_args: abi_encode_args.join(", "), + struct_fields: struct_fields.join("; "), + } + }); + + Self { + contract_id, + path: path.to_path_buf(), + name: contract.name.to_string(), + constructor_data, + artifact, + } + } + + /// If contract has a non-empty constructor, generates a helper source file for it containing a + /// helper to encode constructor arguments. + /// + /// This is needed because current preprocessing wraps the arguments, leaving them unchanged. + /// This allows us to handle nested new expressions correctly. However, this requires us to have + /// a way to wrap both named and unnamed arguments. i.e you can't do abi.encode({arg: val}). + /// + /// This function produces a helper struct + a helper function to encode the arguments. The + /// struct is defined in scope of an abstract contract inheriting the contract containing the + /// constructor. This is done as a hack to allow us to inherit the same scope of definitions. + /// + /// The resulted helper looks like this: + /// ```solidity + /// import "lib/openzeppelin-contracts/contracts/token/ERC20.sol"; + /// + /// abstract contract DeployHelper335 is ERC20 { + /// struct ConstructorArgs { + /// string name; + /// string symbol; + /// } + /// } + /// + /// function encodeArgs335(DeployHelper335.ConstructorArgs memory args) pure returns (bytes memory) { + /// return abi.encode(args.name, args.symbol); + /// } + /// ``` + /// + /// Example usage: + /// ```solidity + /// new ERC20(name, symbol) + /// ``` + /// becomes + /// ```solidity + /// vm.deployCode("artifact path", encodeArgs335(DeployHelper335.ConstructorArgs(name, symbol))) + /// ``` + /// With named arguments: + /// ```solidity + /// new ERC20({name: name, symbol: symbol}) + /// ``` + /// becomes + /// ```solidity + /// vm.deployCode("artifact path", encodeArgs335(DeployHelper335.ConstructorArgs({name: name, symbol: symbol}))) + /// ``` + pub fn build_helper(&self) -> Option { + let Self { contract_id, path, name, constructor_data, artifact: _ } = self; + + let Some(constructor_details) = constructor_data else { return None }; + let contract_id = contract_id.get(); + let struct_fields = &constructor_details.struct_fields; + let abi_encode_args = &constructor_details.abi_encode_args; + + let helper = format!( + r#" +pragma solidity >=0.4.0; + +import "{path}"; + +abstract contract DeployHelper{contract_id} is {name} {{ + struct ConstructorArgs {{ + {struct_fields}; + }} +}} + +function encodeArgs{contract_id}(DeployHelper{contract_id}.ConstructorArgs memory args) pure returns (bytes memory) {{ + return abi.encode({abi_encode_args}); +}} + "#, + path = path.to_slash_lossy(), + ); + + Some(helper) + } +} diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs new file mode 100644 index 0000000000000..2af9d76ba61af --- /dev/null +++ b/crates/common/src/preprocessor/deps.rs @@ -0,0 +1,370 @@ +use super::{ + data::{ContractData, PreprocessorData}, + span_to_range, +}; +use foundry_compilers::Updates; +use itertools::Itertools; +use solar_parse::interface::Session; +use solar_sema::{ + hir::{ContractId, Expr, ExprKind, Hir, NamedArg, TypeKind, Visit}, + interface::{data_structures::Never, source_map::FileName, SourceMap}, +}; +use std::{ + collections::{BTreeMap, BTreeSet, HashSet}, + ops::{ControlFlow, Range}, + path::{Path, PathBuf}, +}; + +/// Holds data about referenced source contracts and bytecode dependencies. +pub(crate) struct PreprocessorDependencies { + // Mapping contract id to preprocess -> contract bytecode dependencies. + pub preprocessed_contracts: BTreeMap>, + // Referenced contract ids. + pub referenced_contracts: HashSet, +} + +impl PreprocessorDependencies { + pub fn new( + sess: &Session, + hir: &Hir<'_>, + paths: &[PathBuf], + src_dir: &Path, + root_dir: &Path, + mocks: &mut HashSet, + ) -> Self { + let mut preprocessed_contracts = BTreeMap::new(); + let mut referenced_contracts = HashSet::new(); + for contract_id in hir.contract_ids() { + let contract = hir.contract(contract_id); + let source = hir.source(contract.source); + + let FileName::Real(path) = &source.file.name else { + continue; + }; + + // Collect dependencies only for tests and scripts. + if !paths.contains(path) { + let path = path.display(); + trace!("{path} is not test or script"); + continue; + } + + // 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 = hir.contract(*base_contract_id); + let FileName::Real(path) = &hir.source(base_contract.source).file.name else { + return false; + }; + 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}"); + 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)); + } + + let mut deps_collector = BytecodeDependencyCollector::new( + sess.source_map(), + hir, + source.file.src.as_str(), + src_dir, + ); + // Analyze current contract. + let _ = deps_collector.walk_contract(contract); + // Ignore empty test contracts declared in source files with other contracts. + 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); + } + Self { preprocessed_contracts, referenced_contracts } + } +} + +/// Represents a bytecode dependency kind. +#[derive(Debug)] +enum BytecodeDependencyKind { + /// `type(Contract).creationCode` + CreationCode, + /// `new Contract`. + New { + /// Contract name. + name: String, + /// Constructor args length. + args_length: usize, + /// Constructor call args offset. + call_args_offset: usize, + /// `msg.value` (if any) used when creating contract. + value: Option, + /// `salt` (if any) used when creating contract. + salt: Option, + }, +} + +/// Represents a single bytecode dependency. +#[derive(Debug)] +pub(crate) struct BytecodeDependency { + /// Dependency kind. + kind: BytecodeDependencyKind, + /// Source map location of this dependency. + loc: Range, + /// HIR id of referenced contract. + referenced_contract: ContractId, +} + +/// Walks over contract HIR and collects [`BytecodeDependency`]s and referenced contracts. +struct BytecodeDependencyCollector<'hir> { + /// Source map, used for determining contract item locations. + source_map: &'hir SourceMap, + /// Parsed HIR. + hir: &'hir Hir<'hir>, + /// Source content of current contract. + src: &'hir str, + /// Project source dir, used to determine if referenced contract is a source contract. + src_dir: &'hir Path, + /// Dependencies collected for current contract. + dependencies: Vec, + /// Unique HIR ids of contracts referenced from current contract. + referenced_contracts: HashSet, +} + +impl<'hir> BytecodeDependencyCollector<'hir> { + fn new( + source_map: &'hir SourceMap, + hir: &'hir Hir<'hir>, + src: &'hir str, + src_dir: &'hir Path, + ) -> Self { + Self { + source_map, + hir, + src, + src_dir, + dependencies: vec![], + referenced_contracts: HashSet::default(), + } + } + + /// Collects reference identified as bytecode dependency of analyzed contract. + /// Discards any reference that is not in project src directory (e.g. external + /// libraries or mock contracts that extend source contracts). + fn collect_dependency(&mut self, dependency: BytecodeDependency) { + let contract = self.hir.contract(dependency.referenced_contract); + let source = self.hir.source(contract.source); + let FileName::Real(path) = &source.file.name else { + return; + }; + + if !path.starts_with(self.src_dir) { + let path = path.display(); + trace!("ignore dependency {path}"); + return; + } + + self.referenced_contracts.insert(dependency.referenced_contract); + self.dependencies.push(dependency); + } +} + +impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> { + type BreakValue = Never; + + fn hir(&self) -> &'hir Hir<'hir> { + self.hir + } + + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> ControlFlow { + match &expr.kind { + ExprKind::Call(ty, call_args, named_args) => { + if let ExprKind::New(ty_new) = &ty.kind { + if let TypeKind::Custom(item_id) = ty_new.kind { + if let Some(contract_id) = item_id.as_contract() { + let name_loc = span_to_range(self.source_map, ty_new.span); + let name = &self.src[name_loc]; + + // Calculate offset to remove named args, e.g. for an expression like + // `new Counter {value: 333} ( address(this))` + // the offset will be used to replace `{value: 333} ( ` with `(` + let call_args_offset = if named_args.is_some() && !call_args.is_empty() + { + (call_args.span().lo() - ty_new.span.hi()).to_usize() + } else { + 0 + }; + + let args_len = expr.span.hi() - ty_new.span.hi(); + self.collect_dependency(BytecodeDependency { + kind: BytecodeDependencyKind::New { + name: name.to_string(), + args_length: args_len.to_usize(), + call_args_offset, + value: named_arg( + self.src, + named_args, + "value", + self.source_map, + ), + salt: named_arg(self.src, named_args, "salt", self.source_map), + }, + loc: span_to_range(self.source_map, ty.span), + referenced_contract: contract_id, + }); + } + } + } + } + ExprKind::Member(member_expr, ident) => { + if let ExprKind::TypeCall(ty) = &member_expr.kind { + if let TypeKind::Custom(contract_id) = &ty.kind { + if ident.name.as_str() == "creationCode" { + if let Some(contract_id) = contract_id.as_contract() { + self.collect_dependency(BytecodeDependency { + kind: BytecodeDependencyKind::CreationCode, + loc: span_to_range(self.source_map, expr.span), + referenced_contract: contract_id, + }); + } + } + } + } + } + _ => {} + } + self.walk_expr(expr) + } +} + +/// Helper function to extract value of a given named arg. +fn named_arg( + src: &str, + named_args: &Option<&[NamedArg<'_>]>, + arg: &str, + source_map: &SourceMap, +) -> Option { + named_args.unwrap_or_default().iter().find(|named_arg| named_arg.name.as_str() == arg).map( + |named_arg| { + let named_arg_loc = span_to_range(source_map, named_arg.value.span); + src[named_arg_loc].to_string() + }, + ) +} + +/// Goes over all test/script files and replaces bytecode dependencies with cheatcode +/// invocations. +pub(crate) fn remove_bytecode_dependencies( + hir: &Hir<'_>, + deps: &PreprocessorDependencies, + data: &PreprocessorData, +) -> Updates { + let mut updates = Updates::default(); + for (contract_id, deps) in &deps.preprocessed_contracts { + let contract = hir.contract(*contract_id); + let source = hir.source(contract.source); + let FileName::Real(path) = &source.file.name else { + continue; + }; + + let updates = updates.entry(path.clone()).or_default(); + let mut used_helpers = BTreeSet::new(); + + let vm_interface_name = format!("VmContractHelper{}", contract_id.get()); + // `address(uint160(uint256(keccak256("hevm cheat code"))))` + let vm = format!("{vm_interface_name}(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)"); + + for dep in deps { + let Some(ContractData { artifact, constructor_data, .. }) = + data.get(&dep.referenced_contract) + else { + continue; + }; + + match &dep.kind { + BytecodeDependencyKind::CreationCode => { + // for creation code we need to just call getCode + updates.insert(( + dep.loc.start, + dep.loc.end, + format!("{vm}.getCode(\"{artifact}\")"), + )); + } + BytecodeDependencyKind::New { + name, + args_length, + call_args_offset, + value, + salt, + } => { + let mut update = format!("{name}(payable({vm}.deployCode({{"); + update.push_str(&format!("_artifact: \"{artifact}\"")); + + if let Some(value) = value { + update.push_str(", "); + update.push_str(&format!("_value: {value}")); + } + + if let Some(salt) = salt { + update.push_str(", "); + update.push_str(&format!("_salt: {salt}")); + } + + if constructor_data.is_some() { + // Insert our helper + used_helpers.insert(dep.referenced_contract); + + update.push_str(", "); + update.push_str(&format!( + "_args: encodeArgs{id}(DeployHelper{id}.ConstructorArgs", + id = dep.referenced_contract.get() + )); + if *call_args_offset > 0 { + update.push('('); + } + updates.insert((dep.loc.start, dep.loc.end + call_args_offset, update)); + updates.insert(( + dep.loc.end + args_length, + dep.loc.end + args_length, + ")})))".to_string(), + )); + } else { + update.push_str("})))"); + updates.insert((dep.loc.start, dep.loc.end + args_length, update)); + } + } + }; + } + let helper_imports = used_helpers.into_iter().map(|id| { + let id = id.get(); + format!( + "import {{DeployHelper{id}, encodeArgs{id}}} from \"foundry-pp/DeployHelper{id}.sol\";", + ) + }).join("\n"); + updates.insert(( + source.file.src.len(), + source.file.src.len(), + format!( + r#" +{helper_imports} + +interface {vm_interface_name} {{ + function deployCode(string memory _artifact) external returns (address); + function deployCode(string memory _artifact, bytes32 _salt) external returns (address); + function deployCode(string memory _artifact, bytes memory _args) external returns (address); + function deployCode(string memory _artifact, bytes memory _args, bytes32 _salt) external returns (address); + function deployCode(string memory _artifact, uint256 _value) external returns (address); + function deployCode(string memory _artifact, uint256 _value, bytes32 _salt) external returns (address); + function deployCode(string memory _artifact, bytes memory _args, uint256 _value) external returns (address); + function deployCode(string memory _artifact, bytes memory _args, uint256 _value, bytes32 _salt) external returns (address); + function getCode(string memory _artifact) external returns (bytes memory); +}}"# + ), + )); + } + updates +} diff --git a/crates/common/src/preprocessor/mod.rs b/crates/common/src/preprocessor/mod.rs new file mode 100644 index 0000000000000..ff8b0e66feae3 --- /dev/null +++ b/crates/common/src/preprocessor/mod.rs @@ -0,0 +1,157 @@ +use foundry_compilers::{ + apply_updates, + artifacts::SolcLanguage, + error::Result, + multi::{MultiCompiler, MultiCompilerInput, MultiCompilerLanguage}, + project::Preprocessor, + solc::{SolcCompiler, SolcVersionedInput}, + Compiler, Language, ProjectPathsConfig, +}; +use solar_parse::{ + ast::Span, + interface::{Session, SourceMap}, +}; +use solar_sema::{thread_local::ThreadLocal, ParsingContext}; +use std::{collections::HashSet, ops::Range, path::PathBuf}; + +mod data; +use data::{collect_preprocessor_data, create_deploy_helpers}; + +mod deps; +use deps::{remove_bytecode_dependencies, PreprocessorDependencies}; + +/// Returns the range of the given span in the source map. +#[track_caller] +fn span_to_range(source_map: &SourceMap, span: Span) -> Range { + source_map.span_to_source(span).unwrap().1 +} + +#[derive(Debug)] +pub struct TestOptimizerPreprocessor; + +impl Preprocessor for TestOptimizerPreprocessor { + fn preprocess( + &self, + _solc: &SolcCompiler, + input: &mut SolcVersionedInput, + paths: &ProjectPathsConfig, + mocks: &mut HashSet, + ) -> Result<()> { + // Skip if we are not preprocessing any tests or scripts. Avoids unnecessary AST parsing. + if !input.input.sources.iter().any(|(path, _)| paths.is_test_or_script(path)) { + trace!("no tests or sources to preprocess"); + return Ok(()); + } + + let sess = solar_session_from_solc(input); + let _ = sess.enter_parallel(|| -> solar_parse::interface::Result { + // Set up the parsing context with the project paths. + let mut parsing_context = solar_pcx_from_solc_no_sources(&sess, input, paths); + + // Add the sources into the context. + // Include all sources in the source map so as to not re-load them from disk, but only + // parse and preprocess tests and scripts. + let mut preprocessed_paths = vec![]; + let sources = &mut input.input.sources; + for (path, source) in sources.iter() { + if let Ok(src_file) = + sess.source_map().new_source_file(path.clone(), source.content.as_str()) + { + if paths.is_test_or_script(path) { + parsing_context.add_file(src_file); + preprocessed_paths.push(path.clone()); + } + } + } + + // Parse and preprocess. + let hir_arena = ThreadLocal::new(); + if let Some(gcx) = parsing_context.parse_and_lower(&hir_arena)? { + let hir = &gcx.get().hir; + // Collect tests and scripts dependencies and identify mock contracts. + let deps = PreprocessorDependencies::new( + &sess, + hir, + &preprocessed_paths, + &paths.paths_relative().sources, + &paths.root, + mocks, + ); + // Collect data of source contracts referenced in tests and scripts. + let data = collect_preprocessor_data(&sess, hir, &deps.referenced_contracts); + + // Extend existing sources with preprocessor deploy helper sources. + sources.extend(create_deploy_helpers(&data)); + + // Generate and apply preprocessor source updates. + apply_updates(sources, remove_bytecode_dependencies(hir, &deps, &data)); + } + + Ok(()) + }); + + // Warn if any diagnostics emitted during content parsing. + if let Err(err) = sess.emitted_errors().unwrap() { + warn!("failed preprocessing {err}"); + } + + Ok(()) + } +} + +impl Preprocessor for TestOptimizerPreprocessor { + fn preprocess( + &self, + compiler: &MultiCompiler, + input: &mut ::Input, + paths: &ProjectPathsConfig, + mocks: &mut HashSet, + ) -> Result<()> { + // Preprocess only Solc compilers. + let MultiCompilerInput::Solc(input) = input else { return Ok(()) }; + + let Some(solc) = &compiler.solc else { return Ok(()) }; + + let paths = paths.clone().with_language::(); + self.preprocess(solc, input, &paths, mocks) + } +} + +fn solar_session_from_solc(solc: &SolcVersionedInput) -> Session { + use solar_parse::interface::config; + + Session::builder() + .with_buffer_emitter(Default::default()) + .opts(config::Opts { + language: match solc.input.language { + SolcLanguage::Solidity => config::Language::Solidity, + SolcLanguage::Yul => config::Language::Yul, + _ => unimplemented!(), + }, + + // TODO: ... + /* + evm_version: solc.input.settings.evm_version, + */ + ..Default::default() + }) + .build() +} + +fn solar_pcx_from_solc_no_sources<'sess>( + sess: &'sess Session, + solc: &SolcVersionedInput, + paths: &ProjectPathsConfig, +) -> ParsingContext<'sess> { + let mut pcx = ParsingContext::new(sess); + pcx.file_resolver.set_current_dir(solc.cli_settings.base_path.as_ref().unwrap_or(&paths.root)); + for remapping in &paths.remappings { + pcx.file_resolver.add_import_remapping(solar_sema::interface::config::ImportRemapping { + context: remapping.context.clone().unwrap_or_default(), + prefix: remapping.name.clone(), + path: remapping.path.clone(), + }); + } + pcx.file_resolver.add_include_paths(solc.cli_settings.include_paths.iter().cloned()); + pcx +} From 26d53543a95c8222dd86a196c1abce39bce922d9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:21:33 +0200 Subject: [PATCH 21/27] bump --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16c5c68e027f3..9283b11bc38e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3876,7 +3876,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" +source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3889,7 +3889,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.14.0", + "itertools 0.13.0", "md-5", "path-slash", "rand 0.8.5", @@ -3913,7 +3913,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" +source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3922,7 +3922,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" +source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3945,7 +3945,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" +source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3959,7 +3959,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=5377589#53775896584b58e58dcbd14af2128675b81a933b" +source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" dependencies = [ "alloy-primitives", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index e60aa2bbdde9f..bd58037d69b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -313,7 +313,7 @@ yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } path-slash = "0.2" [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "5377589" } +foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "743ff47" } # foundry-compilers = { path = "../compilers/crates/compilers" } solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } From 503cc449665f0041fd6ce225cf502989e9d0b326 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 3 Apr 2025 18:46:12 +0300 Subject: [PATCH 22/27] Ensure no cached artifacts in projects --- crates/cast/tests/cli/main.rs | 1 + crates/forge/tests/cli/test_optimizer.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index c0f636175018d..7abe599c37fe1 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1934,6 +1934,7 @@ forgetest_async!(decode_traces_with_project_artifacts, |prj, cmd| { anvil::spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; foundry_test_utils::util::initialize(prj.root()); + prj.clear(); prj.add_source( "LocalProjectContract", r#" diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c81f06f49c5bf..617f802384198 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -2,6 +2,7 @@ // Test cache is invalidated when `forge build` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { + prj.clear(); // All files are built with optimized tests. cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -32,6 +33,7 @@ Compiling 22 files with [..] // Test cache is invalidated when `forge test` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { + prj.clear(); // All files are built with optimized tests. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -67,6 +69,7 @@ Compiling 20 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -191,6 +194,7 @@ Compiling 1 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -328,6 +332,7 @@ Compiling 1 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -482,6 +487,7 @@ Compiling 2 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -618,6 +624,7 @@ Compiling 2 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -730,6 +737,7 @@ Compiling 2 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( @@ -994,6 +1002,7 @@ Compiling 1 files with [..] // Test preprocessing contracts with payable constructor, value and salt named args. forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { + prj.clear(); prj.wipe_contracts(); prj.add_source( From e005150481aa57464e3b0caa5b01ce54041b00f7 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 3 Apr 2025 20:16:40 +0300 Subject: [PATCH 23/27] Disable by default --- crates/cast/tests/cli/main.rs | 1 - crates/cli/src/opts/build/core.rs | 10 +++---- crates/config/src/lib.rs | 2 +- crates/forge/tests/cli/cmd.rs | 1 - crates/forge/tests/cli/config.rs | 6 ++-- crates/forge/tests/cli/test_cmd.rs | 12 -------- crates/forge/tests/cli/test_optimizer.rs | 36 ++++++++++++++++++------ 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 7abe599c37fe1..c0f636175018d 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1934,7 +1934,6 @@ forgetest_async!(decode_traces_with_project_artifacts, |prj, cmd| { anvil::spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; foundry_test_utils::util::initialize(prj.root()); - prj.clear(); prj.add_source( "LocalProjectContract", r#" diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 8f35d918de089..652e3973d3276 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -34,10 +34,10 @@ pub struct BuildOpts { #[serde(skip)] pub no_cache: bool, - /// Disable dynamic test linking. - #[arg(long)] + /// Enable dynamic test linking. + #[arg(long, conflicts_with = "no_cache")] #[serde(skip)] - pub no_dynamic_test_linking: bool, + pub dynamic_test_linking: bool, /// Set pre-linked libraries. #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] @@ -250,8 +250,8 @@ impl Provider for BuildOpts { dict.insert("cache".to_string(), false.into()); } - if self.no_dynamic_test_linking || self.no_cache { - dict.insert("dynamic_test_linking".to_string(), false.into()); + if self.dynamic_test_linking { + dict.insert("dynamic_test_linking".to_string(), true.into()); } if self.build_info { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index c855193c86012..681917cbab9d4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2281,7 +2281,7 @@ impl Default for Config { out: "out".into(), libs: vec!["lib".into()], cache: true, - dynamic_test_linking: true, + dynamic_test_linking: false, cache_path: "cache".into(), broadcast: "broadcast".into(), snapshots: "snapshots".into(), diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 274784101bc8b..f845981bda592 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -3602,7 +3602,6 @@ forgetest!(inspect_custom_counter_method_identifiers, |prj, cmd| { // checks that `clean` also works with the "out" value set in Config forgetest_init!(gas_report_include_tests, |prj, cmd| { prj.update_config(|config| { - config.dynamic_test_linking = false; config.gas_reports_include_tests = true; config.fuzz.runs = 1; }); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 755e89fb583da..5308a2918a6aa 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -42,7 +42,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { out: "out-test".into(), libs: vec!["lib-test".into()], cache: true, - dynamic_test_linking: true, + dynamic_test_linking: false, cache_path: "test-cache".into(), snapshots: "snapshots".into(), gas_snapshot_check: false, @@ -972,7 +972,7 @@ remappings = ["forge-std/=lib/forge-std/src/"] auto_detect_remappings = true libraries = [] cache = true -dynamic_test_linking = true +dynamic_test_linking = false cache_path = "cache" snapshots = "snapshots" gas_snapshot_check = false @@ -1130,7 +1130,7 @@ exclude = [] "auto_detect_remappings": true, "libraries": [], "cache": true, - "dynamic_test_linking": true, + "dynamic_test_linking": false, "cache_path": "cache", "snapshots": "snapshots", "gas_snapshot_check": false, diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 76c299a329e0b..a13f8c51ba81a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2337,9 +2337,6 @@ Logs: // forgetest_init!(metadata_bytecode_traces, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = false; - }); prj.add_source( "ParentProxy.sol", r#" @@ -2898,9 +2895,6 @@ Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] // Tests that test traces display state changes when running with verbosity. #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(should_show_state_changes, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = false; - }); cmd.args(["test", "--mt", "test_Increment", "-vvvvv"]).assert_success().stdout_eq(str![[r#" ... Ran 1 test for test/Counter.t.sol:CounterTest @@ -3000,9 +2994,6 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(colored_traces, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = false; - }); cmd.args(["test", "--mt", "test_Increment", "--color", "always", "-vvvvv"]) .assert_success() .stdout_eq(file!["../fixtures/colored_traces.svg": TermSvg]); @@ -3012,9 +3003,6 @@ forgetest_init!(colored_traces, |prj, cmd| { // #[cfg(not(feature = "isolate-by-default"))] forgetest_init!(should_only_show_failed_tests_trace, |prj, cmd| { - prj.update_config(|config| { - config.dynamic_test_linking = false; - }); prj.add_test( "SuppressTracesTest.t.sol", r#" diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 617f802384198..ed606e8353d04 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -2,7 +2,9 @@ // Test cache is invalidated when `forge build` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_build, |prj, cmd| { - prj.clear(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); // All files are built with optimized tests. cmd.args(["build"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -33,7 +35,9 @@ Compiling 22 files with [..] // Test cache is invalidated when `forge test` if optimize test option toggled. forgetest_init!(toggle_invalidate_cache_on_test, |prj, cmd| { - prj.clear(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); // All files are built with optimized tests. cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" ... @@ -69,8 +73,10 @@ Compiling 20 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_contract_with_no_interface, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", @@ -194,8 +200,10 @@ Compiling 1 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_contract_with_interface, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "interface/CounterIf.sol", @@ -332,8 +340,10 @@ Compiling 1 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_without_inheritance, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", @@ -487,8 +497,10 @@ Compiling 2 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_with_inheritance, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", @@ -624,8 +636,10 @@ Compiling 2 files with [..] // └── mock // └── CounterMock.sol forgetest_init!(preprocess_mock_to_non_mock, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", @@ -737,8 +751,10 @@ Compiling 2 files with [..] // └── test // └── Counter.t.sol forgetest_init!(preprocess_multiple_contracts_with_constructors, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", @@ -1002,8 +1018,10 @@ Compiling 1 files with [..] // Test preprocessing contracts with payable constructor, value and salt named args. forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { - prj.clear(); prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); prj.add_source( "Counter.sol", From e3d2677aa6f45a401dc21d824fec7952949a9eff Mon Sep 17 00:00:00 2001 From: grandizzy Date: Fri, 4 Apr 2025 11:58:48 +0300 Subject: [PATCH 24/27] Handle constructor expectRevert --- crates/cheatcodes/src/fs.rs | 29 ++--- crates/forge/tests/cli/test_optimizer.rs | 135 +++++++++++++++++++++++ 2 files changed, 151 insertions(+), 13 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 554b30e27cc8b..940d5f6d8ef3c 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -371,19 +371,22 @@ fn deploy_code( revm::primitives::CreateScheme::Create }; - let address = executor - .exec_create( - CreateInputs { - caller: ccx.caller, - scheme, - value: value.unwrap_or(U256::ZERO), - init_code: bytecode.into(), - gas_limit: ccx.gas_limit, - }, - ccx, - )? - .address - .ok_or_else(|| fmt_err!("contract creation failed"))?; + let outcome = executor.exec_create( + CreateInputs { + caller: ccx.caller, + scheme, + value: value.unwrap_or(U256::ZERO), + init_code: bytecode.into(), + gas_limit: ccx.gas_limit, + }, + ccx, + )?; + + if !outcome.result.result.is_ok() { + return Err(crate::Error::from(outcome.result.output)) + } + + let address = outcome.address.ok_or_else(|| fmt_err!("contract creation failed"))?; Ok(address.abi_encode()) } diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index ed606e8353d04..52e6e3da799ff 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1165,3 +1165,138 @@ Compiling 1 files with [..] "#]]); }); + +// Counter contract with constructor reverts and emitted events. +forgetest_init!(preprocess_contract_with_require_and_emit, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.dynamic_test_linking = true; + }); + + prj.add_source( + "Counter.sol", + r#" +contract Counter { + event CounterCreated(uint256 number); + uint256 public number; + + constructor(uint256 no) { + require(no != 1, "ctor revert"); + emit CounterCreated(10); + } +} + "#, + ) + .unwrap(); + + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + function test_assert_constructor_revert() public { + vm.expectRevert("ctor revert"); + new Counter(1); + } + + function test_assert_constructor_emit() public { + vm.expectEmit(true, true, true, true); + emit Counter.CounterCreated(10); + + new Counter(11); + } +} + "#, + ) + .unwrap(); + // All 20 files are compiled on first run. + cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#" +... +Compiling 20 files with [..] +... + +"#]]); + + // Change Counter implementation to revert with different message. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + event CounterCreated(uint256 number); + uint256 public number; + + constructor(uint256 no) { + require(no != 1, "ctor revert update"); + emit CounterCreated(10); + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and revert test fails. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[PASS] test_assert_constructor_emit() (gas: [..]) +[FAIL: Error != expected error: ctor revert update != ctor revert] test_assert_constructor_revert() (gas: [..]) +... + +"#]]); + + // Change Counter implementation and don't revert. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + event CounterCreated(uint256 number); + uint256 public number; + + constructor(uint256 no) { + require(no != 0, "ctor revert"); + emit CounterCreated(10); + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and revert test fails. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[PASS] test_assert_constructor_emit() (gas: [..]) +[FAIL: next call did not revert as expected] test_assert_constructor_revert() (gas: [..]) +... + +"#]]); + + // Change Counter implementation to emit different event. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + event CounterCreated(uint256 number); + uint256 public number; + + constructor(uint256 no) { + require(no != 0, "ctor revert"); + emit CounterCreated(100); + } +} + "#, + ) + .unwrap(); + // Assert that only 1 file is compiled (Counter source contract) and emit test fails. + cmd.with_no_redact().assert_failure().stdout_eq(str![[r#" +... +Compiling 1 files with [..] +... +[FAIL: expected an emit, but no logs were emitted afterwards. you might have mismatched events or not enough events were emitted] test_assert_constructor_emit() (gas: [..]) +[FAIL: next call did not revert as expected] test_assert_constructor_revert() (gas: [..]) +... + +"#]]); +}); From 836d315bbe31776215d9c5bcc2556bcb79543884 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Sun, 6 Apr 2025 22:12:23 +0300 Subject: [PATCH 25/27] Collect deps from call args too --- crates/common/src/preprocessor/deps.rs | 6 ++++++ crates/forge/tests/cli/test_optimizer.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs index 2af9d76ba61af..7f9f4bae4765e 100644 --- a/crates/common/src/preprocessor/deps.rs +++ b/crates/common/src/preprocessor/deps.rs @@ -218,6 +218,12 @@ impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> { }); } } + } else { + // Collect deps from call args expressions + // `Interface(address(new Contract()))` + for call_arg_expr in call_args.exprs() { + let _ = self.visit_expr(call_arg_expr); + } } } ExprKind::Member(member_expr, ident) => { diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 52e6e3da799ff..ec95aa12c7cac 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -248,7 +248,7 @@ contract CounterTest is Test { Counter public counter; function setUp() public { - counter = new Counter(); + counter = Counter(address(new Counter())); counter.setNumber(0); } @@ -1075,7 +1075,7 @@ import {CounterWithSalt} from "src/CounterWithSalt.sol"; contract CounterTest is Test { function test_Increment_In_Counter() public { - Counter counter = new Counter{value: 111}(1); + Counter counter = Counter(address(new Counter{value: 111}(1))); counter.increment(); assertEq(counter.number(), 112); } From 1592db4306e07fd1725ca65042beb5ffa37ece7f Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 7 Apr 2025 08:27:08 +0300 Subject: [PATCH 26/27] Bump solar, use upstream fix for call args deps --- Cargo.lock | 16 ++++++++-------- crates/common/src/preprocessor/deps.rs | 6 ------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 657d81d431839..bea661138f677 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3890,7 +3890,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.13.0", + "itertools 0.14.0", "md-5", "path-slash", "rand 0.8.5", @@ -8440,7 +8440,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "alloy-primitives", "bumpalo", @@ -8459,7 +8459,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "strum 0.27.1", ] @@ -8467,7 +8467,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "bumpalo", "index_vec", @@ -8481,7 +8481,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "annotate-snippets", "anstream", @@ -8509,7 +8509,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "proc-macro2", "quote", @@ -8519,7 +8519,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8539,7 +8539,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#b6cf2a037849c6f6e6f505521c79bcf9e576fb57" +source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs index 7f9f4bae4765e..2af9d76ba61af 100644 --- a/crates/common/src/preprocessor/deps.rs +++ b/crates/common/src/preprocessor/deps.rs @@ -218,12 +218,6 @@ impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> { }); } } - } else { - // Collect deps from call args expressions - // `Interface(address(new Contract()))` - for call_arg_expr in call_args.exprs() { - let _ = self.visit_expr(call_arg_expr); - } } } ExprKind::Member(member_expr, ident) => { From 093ef49d22d8ea3f336a832ebea754a97949fe04 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Mon, 7 Apr 2025 21:39:10 +0300 Subject: [PATCH 27/27] Bump solar, compilers and block explorers --- Cargo.lock | 66 ++++++++++++++++++++++++++++++++---------------------- Cargo.toml | 16 ++++--------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63c755a0469cd..c8e6545369bf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3686,9 +3686,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.11.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fa56da41a30ea92956a1a19b66ab0e69b31c158fed5c7aabd2d564bfcfe809" +checksum = "f13025e2bf7b3975b34190a7e0fb66b6a5df61e29e54d1da093938073e06ad10" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3876,8 +3876,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cc93d4e235ddde616d9fc532bac2cf9840203c3432e24fa48d827a4eb51b35c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3913,8 +3914,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce306d3199cbea0805df69b62feb4311d5cbcfe0b2d0fe02819a7300e5c42b" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3922,8 +3924,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8217869394db052366fc6ed03475929e5c844222885c76d5dfcbb95fecf2640b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3945,8 +3948,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750e309e1ec20a4fd73617bdbf25529688ff75ae54c79b17b6428e2ce54ca6e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3959,8 +3963,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.13.5" -source = "git+https://github.com/foundry-rs/compilers?rev=743ff47#743ff47ab7157bddbfe58cad937c9acc1465463b" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0f2673de9b861d8d7f21a48182f3cb724354e17be084655b5b84d4446f41fc" dependencies = [ "alloy-primitives", "cfg-if", @@ -8439,8 +8444,9 @@ dependencies = [ [[package]] name = "solar-ast" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a583a12e73099d1f54bfe7c8a30d7af5ff3591c61ee51cce91045ee5496d86" dependencies = [ "alloy-primitives", "bumpalo", @@ -8458,16 +8464,18 @@ dependencies = [ [[package]] name = "solar-config" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12642e7e8490d6855a345b5b9d5e55630bd30f54450a909e28f1385b448baada" dependencies = [ "strum 0.27.1", ] [[package]] name = "solar-data-structures" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae8902cc28af53e2ba97c450aff7c59d112a433f9ef98fae808e02e25e6dee6" dependencies = [ "bumpalo", "index_vec", @@ -8480,8 +8488,9 @@ dependencies = [ [[package]] name = "solar-interface" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded5ec7a5cee351c7a428842d273470180cab259c46f52d502ec3ab5484d0c3a" dependencies = [ "annotate-snippets", "anstream", @@ -8508,8 +8517,9 @@ dependencies = [ [[package]] name = "solar-macros" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2c9ff6e00eeeff12eac9d589f1f20413d3b71b9c0c292d1eefbd34787e0836" dependencies = [ "proc-macro2", "quote", @@ -8518,8 +8528,9 @@ dependencies = [ [[package]] name = "solar-parse" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1bc1d0253b0f7f2c7cd25ed7bc5d5e8cac43e717d002398250e0e66e43278b" dependencies = [ "alloy-primitives", "bitflags 2.9.0", @@ -8538,8 +8549,9 @@ dependencies = [ [[package]] name = "solar-sema" -version = "0.1.1" -source = "git+https://github.com/paradigmxyz/solar?branch=main#f49440506327cec4a2b0cea04196f4d0f9e4d6a8" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded4b26fb85a0ae2f3277377236af0884c82f38965a2c51046a53016c8b5f332" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -10818,4 +10830,4 @@ dependencies = [ "log", "once_cell", "simd-adler32", -] \ No newline at end of file +] diff --git a/Cargo.toml b/Cargo.toml index bd58037d69b5a..b7d31f092007d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -188,12 +188,12 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.11.0", default-features = false } -foundry-compilers = { version = "0.13.5", default-features = false } +foundry-block-explorers = { version = "0.13.0", default-features = false } +foundry-compilers = { version = "0.14.0", default-features = false } foundry-fork-db = "0.12" solang-parser = "=0.3.3" -solar-parse = { version = "=0.1.1", default-features = false } -solar-sema = { version = "=0.1.1", default-features = false } +solar-parse = { version = "=0.1.2", default-features = false } +solar-sema = { version = "=0.1.2", default-features = false } ## revm revm = { version = "19.4.0", default-features = false } @@ -313,14 +313,6 @@ yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } path-slash = "0.2" [patch.crates-io] -foundry-compilers = { git = "https://github.com/foundry-rs/compilers", rev = "743ff47" } -# foundry-compilers = { path = "../compilers/crates/compilers" } - -solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } -solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } -# solar-parse = { path = "../../paradigmxyz/solar/crates/parse" } -# solar-sema = { path = "../../paradigmxyz/solar/crates/sema" } - ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } # alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" }