revive_solc_json_interface/standard_json/output/
mod.rs

1//! The `solc --standard-json` output.
2
3use std::collections::BTreeMap;
4
5use serde::Deserialize;
6use serde::Serialize;
7
8#[cfg(feature = "resolc")]
9use crate::standard_json::input::settings::warning::Warning;
10use crate::standard_json::output::error::error_handler::ErrorHandler;
11#[cfg(feature = "resolc")]
12use crate::SolcStandardJsonInputSettingsSelection;
13#[cfg(feature = "resolc")]
14use crate::SolcStandardJsonInputSource;
15#[cfg(feature = "parallel")]
16use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
17
18use self::contract::Contract;
19use self::error::Error as SolcStandardJsonOutputError;
20use self::source::Source;
21
22pub mod contract;
23pub mod error;
24pub mod source;
25
26/// The `solc --standard-json` output.
27#[derive(Debug, Serialize, Deserialize, Clone, Default)]
28pub struct Output {
29    /// The file-contract hashmap.
30    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
31    pub contracts: BTreeMap<String, BTreeMap<String, Contract>>,
32    /// The source code mapping data.
33    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
34    pub sources: BTreeMap<String, Source>,
35    /// The compilation errors and warnings.
36    #[serde(default, skip_serializing_if = "Vec::is_empty")]
37    pub errors: Vec<SolcStandardJsonOutputError>,
38    /// The `solc` compiler version.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub version: Option<String>,
41    /// The `solc` compiler long version.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub long_version: Option<String>,
44    /// The `resolc` compiler version.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub revive_version: Option<String>,
47}
48
49#[cfg(feature = "resolc")]
50impl Output {
51    /// Initializes a standard JSON output.
52    ///
53    /// Is used for projects compiled without `solc`.
54    pub fn new(
55        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
56        messages: &mut Vec<SolcStandardJsonOutputError>,
57    ) -> Self {
58        let sources = sources
59            .keys()
60            .enumerate()
61            .map(|(index, path)| (path.to_owned(), Source::new(index)))
62            .collect::<BTreeMap<String, Source>>();
63
64        Self {
65            contracts: BTreeMap::new(),
66            sources,
67            errors: std::mem::take(messages),
68
69            version: None,
70            long_version: None,
71            revive_version: None,
72        }
73    }
74
75    /// Initializes a standard JSON output with messages.
76    ///
77    /// Is used to emit errors in standard JSON mode.
78    pub fn new_with_messages(messages: Vec<SolcStandardJsonOutputError>) -> Self {
79        Self {
80            contracts: BTreeMap::new(),
81            sources: BTreeMap::new(),
82            errors: messages,
83
84            version: None,
85            long_version: None,
86            revive_version: None,
87        }
88    }
89
90    /// Prunes the output JSON and prints it to stdout.
91    pub fn write_and_exit(
92        mut self,
93        selection_to_prune: SolcStandardJsonInputSettingsSelection,
94    ) -> ! {
95        let sources = self.sources.values_mut().collect::<Vec<&mut Source>>();
96        for source in sources.into_iter() {
97            if selection_to_prune
98                .contains(&crate::SolcStandardJsonInputSettingsSelectionFileFlag::AST)
99            {
100                source.ast = None;
101            }
102        }
103
104        let contracts = self
105            .contracts
106            .values_mut()
107            .flat_map(|contracts| contracts.values_mut())
108            .collect::<Vec<&mut Contract>>();
109        for contract in contracts.into_iter() {
110            if selection_to_prune
111                .contains(&crate::SolcStandardJsonInputSettingsSelectionFileFlag::Metadata)
112            {
113                contract.metadata = serde_json::Value::Null;
114            }
115            if selection_to_prune
116                .contains(&crate::SolcStandardJsonInputSettingsSelectionFileFlag::Yul)
117            {
118                contract.ir_optimized = String::new();
119            }
120            if let Some(ref mut evm) = contract.evm {
121                if selection_to_prune.contains(
122                    &crate::SolcStandardJsonInputSettingsSelectionFileFlag::MethodIdentifiers,
123                ) {
124                    evm.method_identifiers.clear();
125                }
126            }
127        }
128
129        self.contracts.retain(|_, contracts| {
130            contracts.retain(|_, contract| !contract.is_empty());
131            !contracts.is_empty()
132        });
133
134        serde_json::to_writer(std::io::stdout(), &self).expect("Stdout writing error");
135        std::process::exit(revive_common::EXIT_CODE_SUCCESS);
136    }
137
138    /// Traverses the AST and returns the list of additional errors and warnings.
139    pub fn preprocess_ast(
140        &mut self,
141        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
142        suppressed_warnings: &[Warning],
143    ) -> anyhow::Result<()> {
144        let id_paths: BTreeMap<usize, &String> = self
145            .sources
146            .iter()
147            .map(|(path, source)| (source.id, path))
148            .collect();
149
150        #[cfg(feature = "parallel")]
151        let iter = self.sources.par_iter();
152        #[cfg(not(feature = "parallel"))]
153        let iter = self.sources.iter();
154
155        let messages: Vec<SolcStandardJsonOutputError> = iter
156            .flat_map(|(_path, source)| {
157                source
158                    .ast
159                    .as_ref()
160                    .map(|ast| Source::get_messages(ast, &id_paths, sources, suppressed_warnings))
161                    .unwrap_or_default()
162            })
163            .collect();
164        self.errors.extend(messages);
165
166        Ok(())
167    }
168
169    /// Pushes an arbitrary error with path.
170    ///
171    /// Please do not push project-general errors without paths here.
172    pub fn push_error(&mut self, path: Option<String>, error: anyhow::Error) {
173        use crate::standard_json::output::error::source_location::SourceLocation;
174
175        self.errors.push(SolcStandardJsonOutputError::new_error(
176            error,
177            path.map(SourceLocation::new),
178            None,
179        ));
180    }
181}
182
183impl ErrorHandler for Output {
184    fn errors(&self) -> Vec<&SolcStandardJsonOutputError> {
185        self.errors
186            .iter()
187            .filter(|error| error.is_error())
188            .collect()
189    }
190
191    fn take_warnings(&mut self) -> Vec<SolcStandardJsonOutputError> {
192        let warnings = self
193            .errors
194            .iter()
195            .filter(|message| message.is_warning())
196            .cloned()
197            .collect();
198        self.errors.retain(|message| !message.is_warning());
199        warnings
200    }
201}