//! The `solc --standard-json` output. use std::collections::BTreeMap; use serde::Deserialize; use serde::Serialize; #[cfg(feature = "resolc")] use crate::standard_json::input::settings::warning::Warning; use crate::standard_json::output::error::error_handler::ErrorHandler; #[cfg(feature = "resolc")] use crate::SolcStandardJsonInputSettingsSelection; #[cfg(feature = "resolc")] use crate::SolcStandardJsonInputSource; #[cfg(all(feature = "parallel", feature = "resolc"))] use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use self::contract::Contract; use self::error::Error as SolcStandardJsonOutputError; use self::source::Source; pub mod contract; pub mod error; pub mod source; /// The `solc --standard-json` output. #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct Output { /// The file-contract hashmap. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub contracts: BTreeMap>, /// The source code mapping data. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub sources: BTreeMap, /// The compilation errors and warnings. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub errors: Vec, /// The `solc` compiler version. #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, /// The `solc` compiler long version. #[serde(skip_serializing_if = "Option::is_none")] pub long_version: Option, /// The `resolc` compiler version. #[serde(skip_serializing_if = "Option::is_none")] pub revive_version: Option, /// The IR pipeline that produced the output: `"newyork"` or `"yul"`. #[serde(skip_serializing_if = "Option::is_none")] pub resolc_pipeline: Option, } #[cfg(feature = "resolc")] impl Output { /// Initializes a standard JSON output. /// /// Is used for projects compiled without `solc`. pub fn new( sources: &BTreeMap, messages: &mut Vec, ) -> Self { let sources = sources .iter() .enumerate() .map(|(index, (path, source))| { ( path.to_owned(), Source { id: index, ast: source .content() .map(|x| serde_json::to_value(x).unwrap()) .unwrap_or_default(), }, ) }) .collect::>(); Self { contracts: BTreeMap::new(), sources: sources.clone(), errors: std::mem::take(messages), version: None, long_version: None, revive_version: None, resolc_pipeline: None, } } /// Initializes a standard JSON output with messages. /// /// Is used to emit errors in standard JSON mode. pub fn new_with_messages(messages: Vec) -> Self { Self { contracts: BTreeMap::new(), sources: BTreeMap::new(), errors: messages, version: None, long_version: None, revive_version: None, resolc_pipeline: None, } } /// Prunes the output JSON and prints it to stdout. pub fn write_and_exit( mut self, selection_to_prune: SolcStandardJsonInputSettingsSelection, ) -> ! { for (path, source) in self.sources.iter_mut() { if selection_to_prune.contains( path, crate::SolcStandardJsonInputSettingsSelectionFileFlag::AST, ) { source.ast = Default::default(); } } for (path, contracts) in self.contracts.iter_mut() { for contract in contracts.values_mut() { for &flag in crate::SolcStandardJsonInputSettingsSelectionFileFlag::all() { if selection_to_prune.contains(path, flag) { contract.reset_field_by_flag(flag); } } } } self.contracts.retain(|_, contracts| { contracts.retain(|_, contract| !contract.is_empty()); !contracts.is_empty() }); serde_json::to_writer(std::io::stdout(), &self).expect("Stdout writing error"); std::process::exit(revive_common::EXIT_CODE_SUCCESS); } /// Traverses the AST and returns the list of additional errors and warnings. pub fn preprocess_ast( &mut self, sources: &BTreeMap, suppressed_warnings: &[Warning], ) -> anyhow::Result<()> { let id_paths: BTreeMap = self .sources .iter() .map(|(path, source)| (source.id, path)) .collect(); #[cfg(feature = "parallel")] let iter = self.sources.par_iter(); #[cfg(not(feature = "parallel"))] let iter = self.sources.iter(); let messages: Vec = iter .flat_map(|(_path, source)| { Source::get_messages(&source.ast, &id_paths, sources, suppressed_warnings) }) .collect(); self.errors.extend(messages); Ok(()) } /// Pushes an arbitrary error with path. /// /// Please do not push project-general errors without paths here. pub fn push_error(&mut self, path: Option, error: anyhow::Error) { use crate::standard_json::output::error::source_location::SourceLocation; self.errors.push(SolcStandardJsonOutputError::new_error( error, path.map(SourceLocation::new), None, )); } } impl ErrorHandler for Output { fn errors(&self) -> Vec<&SolcStandardJsonOutputError> { self.errors .iter() .filter(|error| error.is_error()) .collect() } fn take_warnings(&mut self) -> Vec { let warnings = self .errors .iter() .filter(|message| message.is_warning()) .cloned() .collect(); self.errors.retain(|message| !message.is_warning()); warnings } }