Fix output selection issues for Foundry (#386)

### Description 

Fixes output selection issues for `Foundry` usage. 

- pruning to look at per file settings
- source to include `ast`


### Note 

`Selection.files` field is reintroduced because that's the way `foundry`
functions. it uses per-file output selection

---------

Co-authored-by: xermicus <cyrill@parity.io>
This commit is contained in:
Pavlo Khrystenko
2025-10-08 12:48:41 +02:00
committed by GitHub
parent 7346d82dfa
commit 93c6387dae
3 changed files with 94 additions and 24 deletions
+3
View File
@@ -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`
@@ -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<String, FileSelection>,
}
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<bool> {
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<Flag>) -> 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))
}
}
@@ -56,14 +56,22 @@ impl Output {
messages: &mut Vec<SolcStandardJsonOutputError>,
) -> 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::<BTreeMap<String, Source>>();
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::<Vec<&mut Contract>>();
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();
}
}
}
}