From dc09f4a38d1627ee542c44e87bc3b8c05bbbb8c1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:17:19 +0100 Subject: [PATCH 1/6] feat: don't request Solc JSON AST unless absolutely necessary --- crates/chisel/src/session_source.rs | 13 +++---- crates/cli/src/utils/mod.rs | 2 +- crates/config/src/lib.rs | 53 +++++++++++++++------------- crates/forge/bin/cmd/coverage.rs | 3 ++ crates/forge/bin/cmd/flatten.rs | 8 ++--- crates/forge/bin/cmd/script/build.rs | 2 +- crates/forge/bin/cmd/script/cmd.rs | 2 +- crates/forge/tests/cli/config.rs | 2 +- 8 files changed, 43 insertions(+), 42 deletions(-) diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index c827454e97c4..47660ba091ae 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -318,15 +318,10 @@ impl SessionSource { let mut sources = Sources::new(); sources.insert(self.file_name.clone(), Source::new(self.to_repl_source())); + let remappings = self.config.foundry_config.get_all_remappings().collect::>(); + // Include Vm.sol if forge-std remapping is not available - if !self.config.no_vm && - !self - .config - .foundry_config - .get_all_remappings() - .into_iter() - .any(|r| r.name.starts_with("forge-std")) - { + if !self.config.no_vm && !remappings.iter().any(|r| r.name.starts_with("forge-std")) { sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE)); } @@ -337,7 +332,7 @@ impl SessionSource { .expect("Solidity source not found"); // get all remappings from the config - compiler_input.settings.remappings = self.config.foundry_config.get_all_remappings(); + compiler_input.settings.remappings = remappings; // We also need to enforce the EVM version that the user has specified. compiler_input.settings.evm_version = Some(self.config.foundry_config.evm_version); diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f900c50b071d..99fcceb3e5fd 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -30,7 +30,7 @@ pub use foundry_config::utils::*; /// Deterministic fuzzer seed used for gas snapshots and coverage reports. /// /// The keccak256 hash of "foundry rulez" -pub static STATIC_FUZZ_SEED: [u8; 32] = [ +pub const STATIC_FUZZ_SEED: [u8; 32] = [ 0x01, 0x00, 0xfa, 0x69, 0xa5, 0xf1, 0x71, 0x0a, 0x95, 0xcd, 0xef, 0x94, 0x88, 0x9b, 0x02, 0x84, 0x5d, 0x64, 0x0b, 0x19, 0xad, 0xf0, 0xe3, 0x57, 0xb8, 0xd4, 0xbe, 0x7d, 0x49, 0xee, 0x70, 0xe6, ]; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 879d3ecfb7df..8ea8d6730f21 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -668,7 +668,7 @@ impl Config { .set_auto_detect(self.is_auto_detect()) .set_offline(self.offline) .set_cached(cached) - .set_build_info(cached & self.build_info) + .set_build_info(cached && self.build_info) .set_no_artifacts(no_artifacts) .build()?; @@ -768,7 +768,7 @@ impl Config { .tests(&self.test) .scripts(&self.script) .artifacts(&self.out) - .libs(self.libs.clone()) + .libs(self.libs.iter()) .remappings(self.get_all_remappings()); if let Some(build_info_path) = &self.build_info_path { @@ -796,8 +796,8 @@ impl Config { /// contracts/tokens/token.sol /// contracts/math/math.sol /// ``` - pub fn get_all_remappings(&self) -> Vec { - self.remappings.iter().map(|m| m.clone().into()).collect() + pub fn get_all_remappings(&self) -> impl Iterator + '_ { + self.remappings.iter().map(|m| m.clone().into()) } /// Returns the configured rpc jwt secret @@ -1025,6 +1025,7 @@ impl Config { /// `extra_output` fields pub fn configured_artifacts_handler(&self) -> ConfigurableArtifacts { let mut extra_output = self.extra_output.clone(); + // Sourcify verification requires solc metadata output. Since, it doesn't // affect the UX & performance of the compiler, output the metadata files // by default. @@ -1034,7 +1035,7 @@ impl Config { extra_output.push(ContractOutputSelection::Metadata); } - ConfigurableArtifacts::new(extra_output, self.extra_output_files.clone()) + ConfigurableArtifacts::new(extra_output, self.extra_output_files.iter().cloned()) } /// Parses all libraries in the form of @@ -1043,28 +1044,30 @@ impl Config { Libraries::parse(&self.libraries) } + /// Returns all libraries with applied remappings. Same as `self.solc_settings()?.libraries`. + pub fn libraries_with_remappings(&self) -> Result { + Ok(self.parsed_libraries()?.with_applied_remappings(&self.project_paths())) + } + /// Returns the configured `solc` `Settings` that includes: - /// - all libraries - /// - the optimizer (including details, if configured) - /// - evm version + /// - all libraries + /// - the optimizer (including details, if configured) + /// - evm version pub fn solc_settings(&self) -> Result { - let libraries = self.parsed_libraries()?.with_applied_remappings(&self.project_paths()); - let optimizer = self.optimizer(); - // By default if no targets are specifically selected the model checker uses all targets. // This might be too much here, so only enable assertion checks. // If users wish to enable all options they need to do so explicitly. let mut model_checker = self.model_checker.clone(); - if let Some(ref mut model_checker_settings) = model_checker { + if let Some(model_checker_settings) = &mut model_checker { if model_checker_settings.targets.is_none() { model_checker_settings.targets = Some(vec![ModelCheckerTarget::Assert]); } } - let mut settings = Settings { - optimizer, + Ok(Settings { + libraries: self.libraries_with_remappings()?, + optimizer: self.optimizer(), evm_version: Some(self.evm_version), - libraries, metadata: Some(SettingsMetadata { use_literal_content: Some(self.use_literal_content), bytecode_hash: Some(self.bytecode_hash), @@ -1072,19 +1075,19 @@ impl Config { }), debug: self.revert_strings.map(|revert_strings| DebuggingSettings { revert_strings: Some(revert_strings), + // Not used. debug_info: Vec::new(), }), model_checker, - ..Default::default() - } - .with_extra_output(self.configured_artifacts_handler().output_selection()) - .with_ast(); - - if self.via_ir { - settings = settings.with_via_ir(); + via_ir: Some(self.via_ir), + // Not used. + stop_after: None, + // Set in project paths. + remappings: Vec::new(), + // Set with `with_extra_output` below. + output_selection: Default::default(), } - - Ok(settings) + .with_extra_output(self.configured_artifacts_handler().output_selection())) } /// Returns the default figment @@ -2882,7 +2885,7 @@ mod tests { // contains additional remapping to the source dir assert_eq!( - config.get_all_remappings(), + config.get_all_remappings().collect::>(), vec![ Remapping::from_str("ds-test/=lib/ds-test/src/").unwrap(), Remapping::from_str("env-lib/=lib/env-lib/").unwrap(), diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 9d2bef5e0edf..e342d5825dc1 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -138,6 +138,9 @@ impl CoverageArgs { project.solc_config.settings.via_ir = None; } + // Coverage analysis requires the Solc AST output. + project.solc_config.settings = project.solc_config.settings.with_ast(); + project }; diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 9831e17cecce..743dbbffd291 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -36,13 +36,13 @@ impl FlattenArgs { // flatten is a subset of `BuildArgs` so we can reuse that to get the config let build_args = CoreBuildArgs { project_paths, ..Default::default() }; - let config = build_args.try_load_config_emit_warnings()?; + let mut project = config.ephemeral_no_artifacts_project()?; - let target_path = dunce::canonicalize(target_path)?; - - let project = config.ephemeral_no_artifacts_project()?; + // `Flattener` uses the typed AST for better flattening results. + project.solc_config.settings = project.solc_config.settings.with_ast(); + let target_path = dunce::canonicalize(target_path)?; let compiler_output = ProjectCompiler::new().files([target_path.clone()]).compile(&project); let flattened = match compiler_output { diff --git a/crates/forge/bin/cmd/script/build.rs b/crates/forge/bin/cmd/script/build.rs index ebd502a92a79..c9f60e5e25fd 100644 --- a/crates/forge/bin/cmd/script/build.rs +++ b/crates/forge/bin/cmd/script/build.rs @@ -59,7 +59,7 @@ impl ScriptArgs { let target = self.find_target(&project, &contracts)?.clone(); script_config.target_contract = Some(target.clone()); - let libraries = script_config.config.solc_settings()?.libraries; + let libraries = script_config.config.libraries_with_remappings()?; let linker = Linker::new(project.root(), contracts); let (highlevel_known_contracts, libraries, predeploy_libraries) = self.link_script_target( diff --git a/crates/forge/bin/cmd/script/cmd.rs b/crates/forge/bin/cmd/script/cmd.rs index c1e0fefb0de3..70fce668017f 100644 --- a/crates/forge/bin/cmd/script/cmd.rs +++ b/crates/forge/bin/cmd/script/cmd.rs @@ -332,7 +332,7 @@ impl ScriptArgs { script_config.sender_nonce = nonce; let target = script_config.target_contract(); - let libraries = script_config.config.solc_settings()?.libraries; + let libraries = script_config.config.libraries_with_remappings()?; let (highlevel_known_contracts, libraries, predeploy_libraries) = self.link_script_target(&linker, libraries, new_sender, nonce, target.clone())?; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 0c540d65a4fc..975fa418c464 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -552,7 +552,7 @@ forgetest_init!( pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); let config = cmd.config(); - let remappings = config.get_all_remappings(); + let remappings = config.get_all_remappings().collect::>(); pretty_assertions::assert_eq!( remappings, vec![ From 96a3496dadc851404b6c9d84fb76646ee5e5545a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:58:41 +0100 Subject: [PATCH 2/6] fix: don't require AST just for the path of a file --- crates/common/src/compile.rs | 30 ++++++++++++++++++++-- crates/common/src/contracts.rs | 10 +++----- crates/forge/bin/cmd/script/build.rs | 37 ++++------------------------ crates/forge/bin/cmd/test/mod.rs | 25 +++++-------------- 4 files changed, 42 insertions(+), 60 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 15debf8a539b..459ae516e90e 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -2,10 +2,10 @@ use crate::{compact_to_contract, glob::GlobMatcher, term::SpinnerReporter, TestFunctionExt}; use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, Color, Table}; -use eyre::Result; +use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ - artifacts::{BytecodeObject, ContractBytecodeSome}, + artifacts::{BytecodeObject, CompactContractBytecode, ContractBytecodeSome}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, Artifact, ArtifactId, FileFilter, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, @@ -281,6 +281,32 @@ pub struct ContractSources { } impl ContractSources { + /// Collects the contract sources and artifacts from the project compile output. + pub fn from_project_output( + output: &ProjectCompileOutput, + root: &Path, + ) -> Result { + let mut sources = ContractSources::default(); + for (id, artifact) in output.artifact_ids() { + if let Some(file_id) = artifact.id { + let abs_path = root.join(&id.path); + let source_code = std::fs::read_to_string(abs_path).wrap_err_with(|| { + format!("failed to read artifact source file for `{}`", id.identifier()) + })?; + let compact = CompactContractBytecode { + abi: artifact.abi.clone(), + bytecode: artifact.bytecode.clone(), + deployed_bytecode: artifact.deployed_bytecode.clone(), + }; + let contract = compact_to_contract(compact)?; + sources.insert(&id, file_id, source_code, contract); + } else { + warn!(id = id.identifier(), "source not found"); + } + } + Ok(sources) + } + /// Inserts a contract into the sources. pub fn insert( &mut self, diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index ed6c6ae20f71..a1b251b768dd 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -2,6 +2,7 @@ use alloy_json_abi::{Event, Function, JsonAbi}; use alloy_primitives::{hex, Address, Selector, B256}; +use eyre::Result; use foundry_compilers::{ artifacts::{CompactContractBytecode, ContractBytecodeSome}, ArtifactId, ProjectPathsConfig, @@ -33,10 +34,7 @@ impl ContractsByArtifact { /// Finds a contract which has the same contract name or identifier as `id`. If more than one is /// found, return error. - pub fn find_by_name_or_identifier( - &self, - id: &str, - ) -> eyre::Result> { + pub fn find_by_name_or_identifier(&self, id: &str) -> Result> { let contracts = self .iter() .filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id) @@ -203,9 +201,7 @@ pub fn get_artifact_path(paths: &ProjectPathsConfig, path: &str) -> PathBuf { } /// Helper function to convert CompactContractBytecode ~> ContractBytecodeSome -pub fn compact_to_contract( - contract: CompactContractBytecode, -) -> eyre::Result { +pub fn compact_to_contract(contract: CompactContractBytecode) -> Result { Ok(ContractBytecodeSome { abi: contract.abi.ok_or_else(|| eyre::eyre!("No contract abi"))?, bytecode: contract.bytecode.ok_or_else(|| eyre::eyre!("No contract bytecode"))?.into(), diff --git a/crates/forge/bin/cmd/script/build.rs b/crates/forge/bin/cmd/script/build.rs index c9f60e5e25fd..4b188d955323 100644 --- a/crates/forge/bin/cmd/script/build.rs +++ b/crates/forge/bin/cmd/script/build.rs @@ -3,11 +3,7 @@ use alloy_primitives::{Address, Bytes}; use eyre::{Context, ContextCompat, Result}; use forge::link::{LinkOutput, Linker}; use foundry_cli::utils::get_cached_entry_by_name; -use foundry_common::{ - compact_to_contract, - compile::{self, ContractSources, ProjectCompiler}, - fs, -}; +use foundry_common::compile::{self, ContractSources, ProjectCompiler}; use foundry_compilers::{ artifacts::{ContractBytecode, ContractBytecodeSome, Libraries}, cache::SolFilesCache, @@ -28,33 +24,10 @@ impl ScriptArgs { /// Compiles the file with auto-detection and compiler params. pub fn build(&mut self, script_config: &mut ScriptConfig) -> Result { let (project, output) = self.get_project_and_output(script_config)?; - let output = output.with_stripped_file_prefixes(project.root()); - - let mut sources: ContractSources = Default::default(); - - let contracts = output - .into_artifacts() - .map(|(id, artifact)| -> Result<_> { - // Sources are only required for the debugger, but it *might* mean that there's - // something wrong with the build and/or artifacts. - if let Some(source) = artifact.source_file() { - let path = source - .ast - .ok_or_else(|| eyre::eyre!("source from artifact has no AST"))? - .absolute_path; - let abs_path = project.root().join(path); - let source_code = fs::read_to_string(abs_path).wrap_err_with(|| { - format!("failed to read artifact source file for `{}`", id.identifier()) - })?; - let contract = artifact.clone().into_contract_bytecode(); - let source_contract = compact_to_contract(contract)?; - sources.insert(&id, source.id, source_code, source_contract); - } else { - warn!(?id, "source not found"); - } - Ok((id, artifact)) - }) - .collect::>()?; + let root = project.root(); + let output = output.with_stripped_file_prefixes(root); + let sources = ContractSources::from_project_output(&output, root)?; + let contracts = output.into_artifacts().collect(); let target = self.find_target(&project, &contracts)?.clone(); script_config.target_contract = Some(target.clone()); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 4f0f9f29940a..d84817659da7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -18,7 +18,6 @@ use foundry_cli::{ utils::{self, LoadConfig}, }; use foundry_common::{ - compact_to_contract, compile::{ContractSources, ProjectCompiler}, evm::EvmArgs, shell, @@ -33,7 +32,7 @@ use foundry_config::{ }; use foundry_debugger::Debugger; use regex::Regex; -use std::{collections::BTreeMap, fs, sync::mpsc::channel}; +use std::{collections::BTreeMap, sync::mpsc::channel}; use watchexec::config::{InitConfig, RuntimeConfig}; use yansi::Paint; @@ -213,28 +212,16 @@ impl TestArgs { let outcome = self.run_tests(runner, config, verbosity, &filter, test_options).await?; if should_debug { - let mut sources = ContractSources::default(); - for (id, artifact) in output_clone.unwrap().into_artifacts() { - // Sources are only required for the debugger, but it *might* mean that there's - // something wrong with the build and/or artifacts. - if let Some(source) = artifact.source_file() { - let path = source - .ast - .ok_or_else(|| eyre::eyre!("Source from artifact has no AST."))? - .absolute_path; - let abs_path = project.root().join(&path); - let source_code = fs::read_to_string(abs_path)?; - let contract = artifact.clone().into_contract_bytecode(); - let source_contract = compact_to_contract(contract)?; - sources.insert(&id, source.id, source_code, source_contract); - } - } - // There is only one test. let Some(test) = outcome.into_tests_cloned().next() else { return Err(eyre::eyre!("no tests were executed")); }; + let sources = ContractSources::from_project_output( + output_clone.as_ref().unwrap(), + project.root(), + )?; + // Run the debugger. let mut builder = Debugger::builder() .debug_arenas(test.result.debug.as_slice()) From c62b846db06c423a26c605d8dc2fe9abacc2ba2f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:03:31 +0100 Subject: [PATCH 3/6] feat: add `--abi` and `abi = ` config values --- crates/cli/src/opts/build/mod.rs | 4 ++ crates/config/README.md | 1 + crates/config/src/lib.rs | 13 +++++- crates/forge/bin/cmd/coverage.rs | 73 +++++++++++++++----------------- crates/forge/bin/cmd/flatten.rs | 7 ++- crates/forge/tests/cli/config.rs | 1 + 6 files changed, 54 insertions(+), 45 deletions(-) diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index d502de2d41b9..d207c4e32e00 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -15,6 +15,10 @@ pub use self::paths::ProjectPathsArgs; #[derive(Clone, Debug, Default, Serialize, Parser)] #[clap(next_help_heading = "Compiler options")] pub struct CompilerArgs { + /// Includes the AST as JSON in the compiler output. + #[clap(long, help_heading = "Compiler options")] + pub ast: bool, + /// The target EVM version. #[clap(long, value_name = "VERSION")] #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/config/README.md b/crates/config/README.md index 2944105d5382..46f349d136b2 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -138,6 +138,7 @@ extra_output_files = [] names = false sizes = false via_ir = false +ast = false # caches storage retrieved locally for certain chains and endpoints # can also be restricted to `chains = ["optimism", "mainnet"]` # by default all endpoints will be cached, alternative options are "remote" for only caching non localhost endpoints and "" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8ea8d6730f21..8675dd444aa2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -318,6 +318,8 @@ pub struct Config { /// If set to true, changes compilation pipeline to go through the Yul intermediate /// representation. pub via_ir: bool, + /// Whether to include the AST as JSON in the compiler output. + pub ast: bool, /// RPC storage caching settings determines what chains and endpoints to cache pub rpc_storage_caching: StorageCachingConfig, /// Disables storage caching entirely. This overrides any settings made in @@ -1064,7 +1066,7 @@ impl Config { } } - Ok(Settings { + let mut settings = Settings { libraries: self.libraries_with_remappings()?, optimizer: self.optimizer(), evm_version: Some(self.evm_version), @@ -1087,7 +1089,13 @@ impl Config { // Set with `with_extra_output` below. output_selection: Default::default(), } - .with_extra_output(self.configured_artifacts_handler().output_selection())) + .with_extra_output(self.configured_artifacts_handler().output_selection()); + + if self.ast { + settings = settings.with_ast(); + } + + Ok(settings) } /// Returns the default figment @@ -1877,6 +1885,7 @@ impl Default for Config { ignored_file_paths: vec![], deny_warnings: false, via_ir: false, + ast: false, rpc_storage_caching: Default::default(), rpc_endpoints: Default::default(), etherscan: Default::default(), diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index e342d5825dc1..a37579fb34e6 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -88,6 +88,9 @@ impl CoverageArgs { // Set fuzz seed so coverage reports are deterministic config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + // Coverage analysis requires the Solc AST output. + config.ast = true; + let (project, output) = self.build(&config)?; p_println!(!self.opts.silent => "Analysing contracts..."); let report = self.prepare(&config, output.clone())?; @@ -99,50 +102,42 @@ impl CoverageArgs { /// Builds the project. fn build(&self, config: &Config) -> Result<(Project, ProjectCompileOutput)> { // Set up the project - let project = { - let mut project = config.ephemeral_no_artifacts_project()?; - - if self.ir_minimum { - // TODO: How to detect solc version if the user does not specify a solc version in - // config case1: specify local installed solc ? - // case2: multiple solc versions used and auto_detect_solc == true - if let Some(SolcReq::Version(version)) = &config.solc { - if *version < Version::new(0, 8, 13) { - return Err(eyre::eyre!( + let mut project = config.ephemeral_no_artifacts_project()?; + if self.ir_minimum { + // TODO: How to detect solc version if the user does not specify a solc version in + // config case1: specify local installed solc ? + // case2: multiple solc versions used and auto_detect_solc == true + if let Some(SolcReq::Version(version)) = &config.solc { + if *version < Version::new(0, 8, 13) { + return Err(eyre::eyre!( "viaIR with minimum optimization is only available in Solidity 0.8.13 and above." )); - } } - - // print warning message - p_println!(!self.opts.silent => "{}", - Paint::yellow( - concat!( - "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, which can result in inaccurate source mappings.\n", - "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", - "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", - "See more:\n", - "https://github.com/foundry-rs/foundry/issues/3357\n" - ))); - - // Enable viaIR with minimum optimization - // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 - // And also in new releases of solidity: - // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 - project.solc_config.settings = - project.solc_config.settings.with_via_ir_minimum_optimization() - } else { - project.solc_config.settings.optimizer.disable(); - project.solc_config.settings.optimizer.runs = None; - project.solc_config.settings.optimizer.details = None; - project.solc_config.settings.via_ir = None; } - // Coverage analysis requires the Solc AST output. - project.solc_config.settings = project.solc_config.settings.with_ast(); - - project - }; + // print warning message + p_println!(!self.opts.silent => "{}", + Paint::yellow( + concat!( + "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, which can result in inaccurate source mappings.\n", + "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", + "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", + "See more:\n", + "https://github.com/foundry-rs/foundry/issues/3357\n" + ))); + + // Enable viaIR with minimum optimization + // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 + // And also in new releases of solidity: + // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 + project.solc_config.settings = + project.solc_config.settings.with_via_ir_minimum_optimization() + } else { + project.solc_config.settings.optimizer.disable(); + project.solc_config.settings.optimizer.runs = None; + project.solc_config.settings.optimizer.details = None; + project.solc_config.settings.via_ir = None; + } let output = ProjectCompiler::default() .compile(&project)? diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 743dbbffd291..b4c1edcd9c5e 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -36,11 +36,10 @@ impl FlattenArgs { // flatten is a subset of `BuildArgs` so we can reuse that to get the config let build_args = CoreBuildArgs { project_paths, ..Default::default() }; - let config = build_args.try_load_config_emit_warnings()?; - let mut project = config.ephemeral_no_artifacts_project()?; - + let mut config = build_args.try_load_config_emit_warnings()?; // `Flattener` uses the typed AST for better flattening results. - project.solc_config.settings = project.solc_config.settings.with_ast(); + config.ast = true; + let project = config.ephemeral_no_artifacts_project()?; let target_path = dunce::canonicalize(target_path)?; let compiler_output = ProjectCompiler::new().files([target_path.clone()]).compile(&project); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 975fa418c464..399d687a703b 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -98,6 +98,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { ignored_file_paths: vec![], deny_warnings: false, via_ir: true, + ast: false, rpc_storage_caching: StorageCachingConfig { chains: CachedChains::None, endpoints: CachedEndpoints::Remote, From 359e80435f68929f895a2720511f74030b0f1234 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:11:07 +0100 Subject: [PATCH 4/6] fmt --- crates/forge/bin/cmd/coverage.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index a37579fb34e6..3505fc318247 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -116,15 +116,14 @@ impl CoverageArgs { } // print warning message - p_println!(!self.opts.silent => "{}", - Paint::yellow( - concat!( - "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, which can result in inaccurate source mappings.\n", - "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", - "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", - "See more:\n", - "https://github.com/foundry-rs/foundry/issues/3357\n" - ))); + let msg = Paint::yellow(concat!( + "Warning! \"--ir-minimum\" flag enables viaIR with minimum optimization, \ + which can result in inaccurate source mappings.\n", + "Only use this flag as a workaround if you are experiencing \"stack too deep\" errors.\n", + "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", + "See more: https://github.com/foundry-rs/foundry/issues/3357", + )); + p_println!(!self.opts.silent => "{msg}"); // Enable viaIR with minimum optimization // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 From 99bbfbfa396fb4e3ef6f200546223c7e343cc5bf Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:24:54 +0100 Subject: [PATCH 5/6] fix config --- crates/cli/src/opts/build/core.rs | 4 ++++ crates/cli/src/opts/build/mod.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 02c14a90fa6e..dd01710bd2f2 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -216,6 +216,10 @@ impl Provider for CoreBuildArgs { dict.insert("build_info".to_string(), self.build_info.into()); } + if self.compiler.ast { + dict.insert("ast".to_string(), true.into()); + } + if self.compiler.optimize { dict.insert("optimizer".to_string(), self.compiler.optimize.into()); } diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index d207c4e32e00..0b97ed2dfe89 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -17,6 +17,7 @@ pub use self::paths::ProjectPathsArgs; pub struct CompilerArgs { /// Includes the AST as JSON in the compiler output. #[clap(long, help_heading = "Compiler options")] + #[serde(skip)] pub ast: bool, /// The target EVM version. From b71363822a40cfa8432275757c453f948fec20a0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 24 Feb 2024 03:01:22 +0100 Subject: [PATCH 6/6] fix: keep AST in build_info --- crates/config/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8675dd444aa2..48a8d386a723 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1091,7 +1091,8 @@ impl Config { } .with_extra_output(self.configured_artifacts_handler().output_selection()); - if self.ast { + // We're keeping AST in `--build-info` for backwards compatibility with HardHat. + if self.ast || self.build_info { settings = settings.with_ast(); }