revive_solc_json_interface/standard_json/output/
source.rs1use serde::Deserialize;
4use serde::Serialize;
5
6use crate::standard_json::output::error::Error as SolcStandardJsonOutputError;
7#[cfg(feature = "resolc")]
8use crate::warning::Warning;
9
10#[derive(Debug, Serialize, Deserialize, Clone)]
12#[serde(rename_all = "camelCase")]
13pub struct Source {
14 pub id: usize,
16 pub ast: Option<serde_json::Value>,
18}
19
20impl Source {
21 pub fn check_ecrecover(ast: &serde_json::Value) -> Option<SolcStandardJsonOutputError> {
23 let ast = ast.as_object()?;
24
25 if ast.get("nodeType")?.as_str()? != "FunctionCall" {
26 return None;
27 }
28
29 let expression = ast.get("expression")?.as_object()?;
30 if expression.get("nodeType")?.as_str()? != "Identifier" {
31 return None;
32 }
33 if expression.get("name")?.as_str()? != "ecrecover" {
34 return None;
35 }
36
37 Some(SolcStandardJsonOutputError::message_ecrecover(
38 ast.get("src")?.as_str(),
39 ))
40 }
41
42 pub fn check_send_and_transfer(ast: &serde_json::Value) -> Option<SolcStandardJsonOutputError> {
44 let ast = ast.as_object()?;
45
46 if ast.get("nodeType")?.as_str()? != "FunctionCall" {
47 return None;
48 }
49
50 let expression = ast.get("expression")?.as_object()?;
51 if expression.get("nodeType")?.as_str()? != "MemberAccess" {
52 return None;
53 }
54 let member_name = expression.get("memberName")?.as_str()?;
55 if member_name != "send" && member_name != "transfer" {
56 return None;
57 }
58
59 Some(SolcStandardJsonOutputError::message_send_and_transfer(
60 ast.get("src")?.as_str(),
61 ))
62 }
63
64 pub fn check_assembly_extcodesize(
66 ast: &serde_json::Value,
67 ) -> Option<SolcStandardJsonOutputError> {
68 let ast = ast.as_object()?;
69
70 if ast.get("nodeType")?.as_str()? != "YulFunctionCall" {
71 return None;
72 }
73 if ast
74 .get("functionName")?
75 .as_object()?
76 .get("name")?
77 .as_str()?
78 != "extcodesize"
79 {
80 return None;
81 }
82
83 Some(SolcStandardJsonOutputError::message_extcodesize(
84 ast.get("src")?.as_str(),
85 ))
86 }
87
88 pub fn check_assembly_origin(ast: &serde_json::Value) -> Option<SolcStandardJsonOutputError> {
90 let ast = ast.as_object()?;
91
92 if ast.get("nodeType")?.as_str()? != "YulFunctionCall" {
93 return None;
94 }
95 if ast
96 .get("functionName")?
97 .as_object()?
98 .get("name")?
99 .as_str()?
100 != "origin"
101 {
102 return None;
103 }
104
105 Some(SolcStandardJsonOutputError::message_tx_origin(
106 ast.get("src")?.as_str(),
107 ))
108 }
109
110 pub fn check_tx_origin(ast: &serde_json::Value) -> Option<SolcStandardJsonOutputError> {
112 let ast = ast.as_object()?;
113
114 if ast.get("nodeType")?.as_str()? != "MemberAccess" {
115 return None;
116 }
117 if ast.get("memberName")?.as_str()? != "origin" {
118 return None;
119 }
120
121 let expression = ast.get("expression")?.as_object()?;
122 if expression.get("nodeType")?.as_str()? != "Identifier" {
123 return None;
124 }
125 if expression.get("name")?.as_str()? != "tx" {
126 return None;
127 }
128
129 Some(SolcStandardJsonOutputError::message_tx_origin(
130 ast.get("src")?.as_str(),
131 ))
132 }
133
134 #[cfg(feature = "resolc")]
136 pub fn get_messages(
137 ast: &serde_json::Value,
138 suppressed_warnings: &[Warning],
139 ) -> Vec<SolcStandardJsonOutputError> {
140 let mut messages = Vec::new();
141 if !suppressed_warnings.contains(&Warning::EcRecover) {
142 if let Some(message) = Self::check_ecrecover(ast) {
143 messages.push(message);
144 }
145 }
146 if !suppressed_warnings.contains(&Warning::SendTransfer) {
147 if let Some(message) = Self::check_send_and_transfer(ast) {
148 messages.push(message);
149 }
150 }
151 if !suppressed_warnings.contains(&Warning::ExtCodeSize) {
152 if let Some(message) = Self::check_assembly_extcodesize(ast) {
153 messages.push(message);
154 }
155 }
156 if !suppressed_warnings.contains(&Warning::TxOrigin) {
157 if let Some(message) = Self::check_assembly_origin(ast) {
158 messages.push(message);
159 }
160 if let Some(message) = Self::check_tx_origin(ast) {
161 messages.push(message);
162 }
163 }
164
165 match ast {
166 serde_json::Value::Array(array) => {
167 for element in array.iter() {
168 messages.extend(Self::get_messages(element, suppressed_warnings));
169 }
170 }
171 serde_json::Value::Object(object) => {
172 for (_key, value) in object.iter() {
173 messages.extend(Self::get_messages(value, suppressed_warnings));
174 }
175 }
176 _ => {}
177 }
178
179 messages
180 }
181
182 pub fn last_contract_name(&self) -> anyhow::Result<String> {
184 self.ast
185 .as_ref()
186 .ok_or_else(|| anyhow::anyhow!("The AST is empty"))?
187 .get("nodes")
188 .and_then(|value| value.as_array())
189 .ok_or_else(|| {
190 anyhow::anyhow!("The last contract cannot be found in an empty list of nodes")
191 })?
192 .iter()
193 .filter_map(
194 |node| match node.get("nodeType").and_then(|node| node.as_str()) {
195 Some("ContractDefinition") => Some(node.get("name")?.as_str()?.to_owned()),
196 _ => None,
197 },
198 )
199 .next_back()
200 .ok_or_else(|| anyhow::anyhow!("The last contract not found in the AST"))
201 }
202}