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