revive_solc_json_interface/standard_json/output/error/
mod.rs

1//! The `solc --standard-json` output error.
2
3use std::collections::BTreeMap;
4
5use serde::Deserialize;
6use serde::Serialize;
7
8use crate::SolcStandardJsonInputSource;
9
10use self::mapped_location::MappedLocation;
11use self::source_location::SourceLocation;
12
13pub mod error_handler;
14pub mod mapped_location;
15pub mod source_location;
16
17/// The `solc --standard-json` output error.
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(rename_all = "camelCase")]
20pub struct Error {
21    /// The component type.
22    pub component: String,
23    /// The error code.
24    pub error_code: Option<String>,
25    /// The formatted error message.
26    pub formatted_message: String,
27    /// The non-formatted error message.
28    pub message: String,
29    /// The error severity.
30    pub severity: String,
31    /// The error location data.
32    pub source_location: Option<SourceLocation>,
33    /// The error type.
34    pub r#type: String,
35}
36
37impl Error {
38    /// The list of ignored `solc` warnings that are strictly EVM-related.
39    pub const IGNORED_WARNING_CODES: [&'static str; 5] = ["1699", "3860", "5159", "5574", "6417"];
40
41    /// A shortcut constructor.
42    pub fn new<S>(
43        r#type: &str,
44        message: S,
45        source_location: Option<SourceLocation>,
46        sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
47    ) -> Self
48    where
49        S: std::fmt::Display,
50    {
51        let message = message.to_string();
52
53        let message_trimmed = message.trim();
54        let mut formatted_message = if message_trimmed.starts_with(r#type) {
55            message_trimmed.to_owned()
56        } else {
57            format!("{type}: {message_trimmed}")
58        };
59        formatted_message.push('\n');
60        if let Some(ref source_location) = source_location {
61            let source_code = sources.and_then(|sources| {
62                sources
63                    .get(source_location.file.as_str())
64                    .and_then(|source| source.content())
65            });
66            let mapped_location =
67                MappedLocation::try_from_source_location(source_location, source_code);
68            formatted_message.push_str(mapped_location.to_string().as_str());
69            formatted_message.push('\n');
70        }
71
72        Self {
73            component: "general".to_owned(),
74            error_code: None,
75            formatted_message,
76            message,
77            severity: r#type.to_lowercase(),
78            source_location,
79            r#type: r#type.to_owned(),
80        }
81    }
82
83    /// A shortcut constructor.
84    pub fn new_error<S>(
85        message: S,
86        source_location: Option<SourceLocation>,
87        sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
88    ) -> Self
89    where
90        S: std::fmt::Display,
91    {
92        Self::new("Error", message, source_location, sources)
93    }
94
95    /// A shortcut constructor.
96    pub fn new_warning<S>(
97        message: S,
98        source_location: Option<SourceLocation>,
99        sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
100    ) -> Self
101    where
102        S: std::fmt::Display,
103    {
104        Self::new("Warning", message, source_location, sources)
105    }
106
107    /// Returns the `<address payable>`'s `send` and `transfer` methods usage error.
108    pub fn warning_send_and_transfer(
109        node: Option<&str>,
110        id_paths: &BTreeMap<usize, &String>,
111        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
112    ) -> Self {
113        let message = r#"
114Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
115Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
116The resolc compiler uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls,
117which disables call re-entrancy and supplies all remaining gas instead of the 2300 gas stipend.
118However, detection is not guaranteed. You are advised to carefully test this, employ
119re-entrancy guards or use the withdrawal pattern instead!
120Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
121and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
122"#
123        .to_owned();
124
125        Self::new_warning(
126            message,
127            node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
128            Some(sources),
129        )
130    }
131
132    /// Returns the `origin` instruction usage warning.
133    pub fn warning_tx_origin(
134        node: Option<&str>,
135        id_paths: &BTreeMap<usize, &String>,
136        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
137    ) -> Self {
138        let message = r#"
139Warning: You are checking for 'tx.origin' in your code, which might lead to unexpected behavior.
140Polkadot comes with native account abstraction support, and therefore the initiator of a
141transaction might be different from the contract calling your code. It is highly recommended NOT
142to rely on tx.origin, but use msg.sender instead.
143"#
144        .to_owned();
145
146        Self::new_warning(
147            message,
148            node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
149            Some(sources),
150        )
151    }
152    /// Returns the `runtimeCode` code usage error.
153    pub fn error_runtime_code(
154        node: Option<&str>,
155        id_paths: &BTreeMap<usize, &String>,
156        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
157    ) -> Self {
158        let message = r#"
159Deploy and runtime code are merged in PVM, accessing `type(T).runtimeCode` is not possible.
160Please consider changing the functionality relying on reading runtime code to a different approach.
161"#;
162
163        Self::new_error(
164            message,
165            node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
166            Some(sources),
167        )
168    }
169
170    /// Appends the contract path to the message..
171    pub fn push_contract_path(&mut self, path: &str) {
172        self.formatted_message
173            .push_str(format!("\n--> {path}\n").as_str());
174    }
175
176    /// Returns true if this is an error.
177    pub fn is_error(&self) -> bool {
178        self.severity == "error"
179    }
180
181    /// Returns true if this is a warning.
182    pub fn is_warning(&self) -> bool {
183        self.severity == "warning"
184    }
185}
186
187impl std::fmt::Display for Error {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        write!(f, "{}", self.formatted_message)
190    }
191}