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(all(feature = "parallel", feature = "resolc"))]
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            .iter()
60            .enumerate()
61            .map(|(index, (path, source))| {
62                (
63                    path.to_owned(),
64                    Source {
65                        id: index,
66                        ast: source.content().map(|x| serde_json::to_value(x).unwrap()),
67                    },
68                )
69            })
70            .collect::<BTreeMap<String, Source>>();
71
72        Self {
73            contracts: BTreeMap::new(),
74            sources: sources.clone(),
75            errors: std::mem::take(messages),
76
77            version: None,
78            long_version: None,
79            revive_version: None,
80        }
81    }
82
83    /// Initializes a standard JSON output with messages.
84    ///
85    /// Is used to emit errors in standard JSON mode.
86    pub fn new_with_messages(messages: Vec<SolcStandardJsonOutputError>) -> Self {
87        Self {
88            contracts: BTreeMap::new(),
89            sources: BTreeMap::new(),
90            errors: messages,
91
92            version: None,
93            long_version: None,
94            revive_version: None,
95        }
96    }
97
98    /// Prunes the output JSON and prints it to stdout.
99    pub fn write_and_exit(
100        mut self,
101        selection_to_prune: SolcStandardJsonInputSettingsSelection,
102    ) -> ! {
103        for (path, contracts) in self.contracts.iter_mut() {
104            for contract in contracts.values_mut() {
105                if selection_to_prune.contains(
106                    path,
107                    &crate::SolcStandardJsonInputSettingsSelectionFileFlag::Metadata,
108                ) {
109                    contract.metadata = serde_json::Value::Null;
110                }
111                if selection_to_prune.contains(
112                    path,
113                    &crate::SolcStandardJsonInputSettingsSelectionFileFlag::Yul,
114                ) {
115                    contract.ir_optimized = String::new();
116                }
117                if let Some(ref mut evm) = contract.evm {
118                    if selection_to_prune.contains(
119                        path,
120                        &crate::SolcStandardJsonInputSettingsSelectionFileFlag::MethodIdentifiers,
121                    ) {
122                        evm.method_identifiers.clear();
123                    }
124                }
125            }
126        }
127
128        self.contracts.retain(|_, contracts| {
129            contracts.retain(|_, contract| !contract.is_empty());
130            !contracts.is_empty()
131        });
132
133        serde_json::to_writer(std::io::stdout(), &self).expect("Stdout writing error");
134        std::process::exit(revive_common::EXIT_CODE_SUCCESS);
135    }
136
137    /// Traverses the AST and returns the list of additional errors and warnings.
138    pub fn preprocess_ast(
139        &mut self,
140        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
141        suppressed_warnings: &[Warning],
142    ) -> anyhow::Result<()> {
143        let id_paths: BTreeMap<usize, &String> = self
144            .sources
145            .iter()
146            .map(|(path, source)| (source.id, path))
147            .collect();
148
149        #[cfg(feature = "parallel")]
150        let iter = self.sources.par_iter();
151        #[cfg(not(feature = "parallel"))]
152        let iter = self.sources.iter();
153
154        let messages: Vec<SolcStandardJsonOutputError> = iter
155            .flat_map(|(_path, source)| {
156                source
157                    .ast
158                    .as_ref()
159                    .map(|ast| Source::get_messages(ast, &id_paths, sources, suppressed_warnings))
160                    .unwrap_or_default()
161            })
162            .collect();
163        self.errors.extend(messages);
164
165        Ok(())
166    }
167
168    /// Pushes an arbitrary error with path.
169    ///
170    /// Please do not push project-general errors without paths here.
171    pub fn push_error(&mut self, path: Option<String>, error: anyhow::Error) {
172        use crate::standard_json::output::error::source_location::SourceLocation;
173
174        self.errors.push(SolcStandardJsonOutputError::new_error(
175            error,
176            path.map(SourceLocation::new),
177            None,
178        ));
179    }
180}
181
182impl ErrorHandler for Output {
183    fn errors(&self) -> Vec<&SolcStandardJsonOutputError> {
184        self.errors
185            .iter()
186            .filter(|error| error.is_error())
187            .collect()
188    }
189
190    fn take_warnings(&mut self) -> Vec<SolcStandardJsonOutputError> {
191        let warnings = self
192            .errors
193            .iter()
194            .filter(|message| message.is_warning())
195            .cloned()
196            .collect();
197        self.errors.retain(|message| !message.is_warning());
198        warnings
199    }
200}