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