Skip to main content

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