Skip to main content

revive_solc_json_interface/standard_json/output/
source.rs

1//! The `solc --standard-json` output source.
2
3#[cfg(feature = "resolc")]
4use std::collections::BTreeMap;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9#[cfg(feature = "resolc")]
10use crate::standard_json::input::settings::warning::Warning;
11#[cfg(feature = "resolc")]
12use crate::standard_json::output::error::Error as SolcStandardJsonOutputError;
13#[cfg(feature = "resolc")]
14use crate::SolcStandardJsonInputSource;
15
16/// The `solc --standard-json` output source.
17#[derive(Debug, Serialize, Deserialize, Clone)]
18#[serde(rename_all = "camelCase")]
19pub struct Source {
20    /// The source code ID.
21    pub id: usize,
22    /// The source code AST.
23    #[serde(default, skip_serializing_if = "serde_json::Value::is_null")]
24    pub ast: serde_json::Value,
25}
26
27#[cfg(feature = "resolc")]
28impl Source {
29    /// Initializes a standard JSON source.
30    ///
31    /// Is used for projects compiled without `solc`.
32    pub fn new(id: usize) -> Self {
33        Self {
34            id,
35            ast: Default::default(),
36        }
37    }
38
39    /// Checks the AST node for the usage of send or transfer address methods.
40    pub fn check_send_and_transfer(
41        ast: &serde_json::Value,
42        id_paths: &BTreeMap<usize, &String>,
43        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
44    ) -> Option<SolcStandardJsonOutputError> {
45        let ast = ast.as_object()?;
46
47        (ast.get("nodeType")?.as_str()? == "FunctionCall").then_some(())?;
48
49        let expression = ast.get("expression")?.as_object()?;
50        (expression.get("nodeType")?.as_str()? == "MemberAccess").then_some(())?;
51        let member_name = expression.get("memberName")?.as_str()?;
52        ["send", "transfer"].contains(&member_name).then_some(())?;
53
54        let expression = expression.get("expression")?.as_object()?;
55        let type_descriptions = expression.get("typeDescriptions")?.as_object()?;
56        let type_identifier = type_descriptions.get("typeIdentifier")?.as_str()?;
57        ["t_address_payable"]
58            .contains(&type_identifier)
59            .then_some(())?;
60
61        Some(Warning::SendAndTransfer.as_error(ast.get("src")?.as_str(), id_paths, sources))
62    }
63
64    /// Checks the AST node for the usage of runtime code.
65    pub fn check_runtime_code(
66        ast: &serde_json::Value,
67        id_paths: &BTreeMap<usize, &String>,
68        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
69    ) -> Option<SolcStandardJsonOutputError> {
70        let ast = ast.as_object()?;
71
72        (ast.get("nodeType")?.as_str()? == "MemberAccess").then_some(())?;
73        (ast.get("memberName")?.as_str()? == "runtimeCode").then_some(())?;
74
75        let expression = ast.get("expression")?.as_object()?;
76        let type_descriptions = expression.get("typeDescriptions")?.as_object()?;
77        type_descriptions
78            .get("typeIdentifier")?
79            .as_str()?
80            .starts_with("t_magic_meta_type")
81            .then_some(())?;
82
83        Some(SolcStandardJsonOutputError::error_runtime_code(
84            ast.get("src")?.as_str(),
85            id_paths,
86            sources,
87        ))
88    }
89
90    /// Checks the AST node for the `tx.origin` value usage.
91    pub fn check_tx_origin(
92        ast: &serde_json::Value,
93        id_paths: &BTreeMap<usize, &String>,
94        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
95    ) -> Option<SolcStandardJsonOutputError> {
96        let ast = ast.as_object()?;
97
98        (ast.get("nodeType")?.as_str()? == "MemberAccess").then_some(())?;
99        (ast.get("memberName")?.as_str()? == "origin").then_some(())?;
100
101        let expression = ast.get("expression")?.as_object()?;
102        (expression.get("nodeType")?.as_str()? == "Identifier").then_some(())?;
103        (expression.get("name")?.as_str()? == "tx").then_some(())?;
104
105        Some(Warning::TxOrigin.as_error(ast.get("src")?.as_str(), id_paths, sources))
106    }
107
108    /// Returns the list of messages for some specific parts of the AST.
109    #[cfg(feature = "resolc")]
110    pub fn get_messages(
111        ast: &serde_json::Value,
112        id_paths: &BTreeMap<usize, &String>,
113        sources: &BTreeMap<String, SolcStandardJsonInputSource>,
114        suppressed_warnings: &[Warning],
115    ) -> Vec<SolcStandardJsonOutputError> {
116        let mut messages = Vec::new();
117        if !suppressed_warnings.contains(&Warning::SendAndTransfer) {
118            if let Some(message) = Self::check_send_and_transfer(ast, id_paths, sources) {
119                messages.push(message);
120            }
121        }
122        if !suppressed_warnings.contains(&Warning::TxOrigin) {
123            if let Some(message) = Self::check_tx_origin(ast, id_paths, sources) {
124                messages.push(message);
125            }
126        }
127        if let Some(message) = Self::check_runtime_code(ast, id_paths, sources) {
128            messages.push(message);
129        }
130
131        match ast {
132            serde_json::Value::Array(array) => {
133                for element in array.iter() {
134                    messages.extend(Self::get_messages(
135                        element,
136                        id_paths,
137                        sources,
138                        suppressed_warnings,
139                    ));
140                }
141            }
142            serde_json::Value::Object(object) => {
143                for (_key, value) in object.iter() {
144                    messages.extend(Self::get_messages(
145                        value,
146                        id_paths,
147                        sources,
148                        suppressed_warnings,
149                    ));
150                }
151            }
152            _ => {}
153        }
154
155        messages
156    }
157}