diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c815b..8398362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Changed +- `standard_json.output_selection` to also look at per file settings. + This is a development pre-release. Supported `polkadot-sdk` rev: `2503.0.1` diff --git a/crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs b/crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs index 3f17599..69a70d0 100644 --- a/crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs +++ b/crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs @@ -2,18 +2,71 @@ pub mod file; +use std::collections::BTreeMap; + use serde::Deserialize; use serde::Serialize; use self::file::flag::Flag; use self::file::File as FileSelection; +/// The `solc --standard-json` per-file output selection. +#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)] +pub struct PerFileSelection { + /// Individual file selection configuration, required for foundry. + #[serde(skip_serializing_if = "BTreeMap::is_empty", flatten)] + pub files: BTreeMap, +} + +impl PerFileSelection { + /// Extends the output selection with another one. + pub fn extend(&mut self, other: Self) { + for (entry, file) in other.files { + self.files + .entry(entry) + .and_modify(|v| { + v.extend(file.clone()); + }) + .or_insert(file); + } + } + + /// Returns flags that are going to be automatically added by the compiler, + /// but were not explicitly requested by the user. + /// + /// Afterwards, the flags are used to prune JSON output before returning it. + pub fn selection_to_prune(&self) -> Self { + let files = self + .files + .iter() + .map(|(k, v)| (k.to_owned(), v.selection_to_prune())) + .collect(); + Self { files } + } + + /// Returns whether `path` contains the `flag` or `None` if there is no selection for `path`. + pub fn contains(&self, path: &String, flag: &Flag) -> Option { + if let Some(file) = self.files.get(path) { + return Some(file.contains(flag)); + }; + None + } + + /// Returns whether this is the empty per file selection. + pub fn is_empty(&self) -> bool { + self.files.is_empty() + } +} + /// The `solc --standard-json` output selection. #[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)] pub struct Selection { /// Only the 'all' wildcard is available for robustness reasons. #[serde(default, rename = "*", skip_serializing_if = "FileSelection::is_empty")] + /// Individual file selection configuration, required for foundry. pub all: FileSelection, + #[serde(skip_serializing_if = "PerFileSelection::is_empty", flatten)] + files: PerFileSelection, } impl Selection { @@ -21,6 +74,7 @@ impl Selection { pub fn new(flags: Vec) -> Self { Self { all: FileSelection::new(flags), + files: Default::default(), } } @@ -38,6 +92,7 @@ impl Selection { pub fn new_required_for_tests() -> Self { Self { all: FileSelection::new_required_for_tests(), + files: Default::default(), } } @@ -49,6 +104,7 @@ impl Selection { /// Extends the output selection with another one. pub fn extend(&mut self, other: Self) -> &mut Self { self.all.extend(other.all); + self.files.extend(other.files); self } @@ -59,11 +115,14 @@ impl Selection { pub fn selection_to_prune(&self) -> Self { Self { all: self.all.selection_to_prune(), + files: self.files.selection_to_prune(), } } /// Whether the flag is requested. - pub fn contains(&self, flag: &Flag) -> bool { - self.all.contains(flag) + pub fn contains(&self, path: &String, flag: &Flag) -> bool { + self.files + .contains(path, flag) + .unwrap_or(self.all.contains(flag)) } } diff --git a/crates/solc-json-interface/src/standard_json/output/mod.rs b/crates/solc-json-interface/src/standard_json/output/mod.rs index 56a9da0..76fd5e1 100644 --- a/crates/solc-json-interface/src/standard_json/output/mod.rs +++ b/crates/solc-json-interface/src/standard_json/output/mod.rs @@ -56,14 +56,22 @@ impl Output { messages: &mut Vec, ) -> Self { let sources = sources - .keys() + .iter() .enumerate() - .map(|(index, path)| (path.to_owned(), Source::new(index))) + .map(|(index, (path, source))| { + ( + path.to_owned(), + Source { + id: index, + ast: source.content().map(|x| serde_json::to_value(x).unwrap()), + }, + ) + }) .collect::>(); Self { contracts: BTreeMap::new(), - sources, + sources: sources.clone(), errors: std::mem::take(messages), version: None, @@ -92,27 +100,27 @@ impl Output { mut self, selection_to_prune: SolcStandardJsonInputSettingsSelection, ) -> ! { - let contracts = self - .contracts - .values_mut() - .flat_map(|contracts| contracts.values_mut()) - .collect::>(); - for contract in contracts.into_iter() { - if selection_to_prune - .contains(&crate::SolcStandardJsonInputSettingsSelectionFileFlag::Metadata) - { - contract.metadata = serde_json::Value::Null; - } - if selection_to_prune - .contains(&crate::SolcStandardJsonInputSettingsSelectionFileFlag::Yul) - { - contract.ir_optimized = String::new(); - } - if let Some(ref mut evm) = contract.evm { + for (path, contracts) in self.contracts.iter_mut() { + for contract in contracts.values_mut() { if selection_to_prune.contains( - &crate::SolcStandardJsonInputSettingsSelectionFileFlag::MethodIdentifiers, + path, + &crate::SolcStandardJsonInputSettingsSelectionFileFlag::Metadata, ) { - evm.method_identifiers.clear(); + contract.metadata = serde_json::Value::Null; + } + if selection_to_prune.contains( + path, + &crate::SolcStandardJsonInputSettingsSelectionFileFlag::Yul, + ) { + contract.ir_optimized = String::new(); + } + if let Some(ref mut evm) = contract.evm { + if selection_to_prune.contains( + path, + &crate::SolcStandardJsonInputSettingsSelectionFileFlag::MethodIdentifiers, + ) { + evm.method_identifiers.clear(); + } } } }