diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 3bd2fa44b9e6a..bbeabe31ac87e 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true workspace = true [dependencies] +serde_ignored = "0.1" foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-compilers = { workspace = true, features = ["svm-solc"] } @@ -56,6 +57,7 @@ path-slash = "0.2" similar-asserts.workspace = true figment = { workspace = true, features = ["test"] } tempfile.workspace = true +tracing-test = "0.2" [features] -isolate-by-default = [] +isolate-by-default = [] \ No newline at end of file diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 1d4a056969a30..da46ab5dca583 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -50,6 +50,8 @@ use std::{ str::FromStr, }; +use tracing::warn; + mod macros; pub mod utils; @@ -640,7 +642,30 @@ impl Config { Self::from_provider(provider) } + /// Read `foundry.toml`, collect any unknown keys, and log a warning. + fn warn_unknown_keys() -> Result<(), ExtractConfigError> { + let path = Path::new(Self::FILE_NAME); + + // if the file exists & is readable, deserialize and collect ignored keys + if let Ok(raw) = fs::read_to_string(path) { + let mut ignored = Vec::new(); + let deserializer = toml::Deserializer::new(&raw); + + // collect every ignored field name + let _: Result = + serde_ignored::deserialize(deserializer, |field| ignored.push(field.to_string())); + + // if we found any, log a single warning with a list + if !ignored.is_empty() { + warn!("Found unknown config keys in {}: {}", Self::FILE_NAME, ignored.join(", ")); + } + } + Ok(()) + } + fn from_figment(figment: Figment) -> Result { + Self::warn_unknown_keys()?; + let mut config = figment.extract::().map_err(ExtractConfigError::new)?; config.profile = figment.profile().clone(); @@ -2580,8 +2605,9 @@ mod tests { }; use similar_asserts::assert_eq; use soldeer_core::remappings::RemappingsLocation; - use std::{fs::File, io::Write}; + use std::{env, fs::File, io::Write}; use tempfile::tempdir; + use tracing_test::traced_test; use NamedChain::Moonbeam; // Helper function to clear `__warnings` in config, since it will be populated during loading @@ -4977,4 +5003,44 @@ mod tests { Ok(()) }); } + + #[traced_test] + #[test] + fn warn_unknown_keys_logs_warning_for_unknown_keys() { + let dir = tempdir().expect("failed to create temp dir"); + let path = dir.path().join("foundry.toml"); + fs::write(&path, "optimizor = false\n").expect("Failed to write foundry.toml"); + + let old_dir = env::current_dir().expect("failed to get current dir"); + env::set_current_dir(&dir).expect("failed to set current dir"); + + let _ = Config::warn_unknown_keys(); + + assert!( + logs_contain("Found unknown config keys") && logs_contain("optimizor"), + "Expected a warning log containing 'optimizor'" + ); + + env::set_current_dir(old_dir).expect("failed to restore dir"); + } + + #[traced_test] + #[test] + fn warn_unknown_keys_logs_nothing_for_valid_keys() { + let dir = tempdir().expect("failed to create temp dir"); + let path = dir.path().join("foundry.toml"); + fs::write(&path, "optimizer = false\n").expect("Failed to write foundry.toml"); + + let old_dir = env::current_dir().expect("failed to get current dir"); + env::set_current_dir(&dir).expect("failed to set current dir"); + + let _ = Config::warn_unknown_keys(); + + assert!( + !logs_contain("Found unknown config keys"), + "Did not expect a warning for only valid keys" + ); + + env::set_current_dir(old_dir).expect("failed to restore dir"); + } }