foundry_compilers_artifacts_solc/ast/
lowfidelity.rs

1//! Bindings for solc's `ast` output field
2
3use crate::serde_helpers;
4use serde::{de::DeserializeOwned, Deserialize, Serialize};
5use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr};
6
7/// Represents the AST field in the solc output
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
9pub struct Ast {
10    #[serde(rename = "absolutePath")]
11    pub absolute_path: String,
12    pub id: usize,
13    #[serde(default, rename = "exportedSymbols")]
14    pub exported_symbols: BTreeMap<String, Vec<usize>>,
15    #[serde(rename = "nodeType")]
16    pub node_type: NodeType,
17    #[serde(with = "serde_helpers::display_from_str")]
18    pub src: SourceLocation,
19    #[serde(default)]
20    pub nodes: Vec<Node>,
21
22    /// Node attributes that were not deserialized.
23    #[serde(flatten)]
24    pub other: BTreeMap<String, serde_json::Value>,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28pub struct Node {
29    /// The node ID.
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub id: Option<usize>,
32
33    /// The node type.
34    #[serde(rename = "nodeType")]
35    pub node_type: NodeType,
36
37    /// The location of the node in the source file.
38    #[serde(with = "serde_helpers::display_from_str")]
39    pub src: SourceLocation,
40
41    /// Child nodes for some node types.
42    #[serde(default)]
43    pub nodes: Vec<Node>,
44
45    /// Body node for some node types.
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub body: Option<Box<Node>>,
48
49    /// Node attributes that were not deserialized.
50    #[serde(flatten)]
51    pub other: BTreeMap<String, serde_json::Value>,
52}
53
54impl Node {
55    /// Deserialize a serialized node attribute.
56    pub fn attribute<D: DeserializeOwned>(&self, key: &str) -> Option<D> {
57        // TODO: Can we avoid this clone?
58        self.other.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
59    }
60}
61
62/// Represents the source location of a node: `<start byte>:<length>:<source index>`.
63///
64/// The `length` and `index` can be -1 which is represented as `None`
65#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
66pub struct SourceLocation {
67    pub start: usize,
68    pub length: Option<usize>,
69    pub index: Option<usize>,
70}
71
72impl FromStr for SourceLocation {
73    type Err = String;
74
75    fn from_str(s: &str) -> Result<Self, Self::Err> {
76        let invalid_location = move || format!("{s} invalid source location");
77
78        let mut split = s.split(':');
79        let start = split
80            .next()
81            .ok_or_else(invalid_location)?
82            .parse::<usize>()
83            .map_err(|_| invalid_location())?;
84        let length = split
85            .next()
86            .ok_or_else(invalid_location)?
87            .parse::<isize>()
88            .map_err(|_| invalid_location())?;
89        let index = split
90            .next()
91            .ok_or_else(invalid_location)?
92            .parse::<isize>()
93            .map_err(|_| invalid_location())?;
94
95        let length = if length < 0 { None } else { Some(length as usize) };
96        let index = if index < 0 { None } else { Some(index as usize) };
97
98        Ok(Self { start, length, index })
99    }
100}
101
102impl fmt::Display for SourceLocation {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        self.start.fmt(f)?;
105        f.write_char(':')?;
106        if let Some(length) = self.length {
107            length.fmt(f)?;
108        } else {
109            f.write_str("-1")?;
110        }
111        f.write_char(':')?;
112        if let Some(index) = self.index {
113            index.fmt(f)?;
114        } else {
115            f.write_str("-1")?;
116        }
117        Ok(())
118    }
119}
120
121#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
122pub enum NodeType {
123    // Expressions
124    Assignment,
125    BinaryOperation,
126    Conditional,
127    ElementaryTypeNameExpression,
128    FunctionCall,
129    FunctionCallOptions,
130    Identifier,
131    IndexAccess,
132    IndexRangeAccess,
133    Literal,
134    MemberAccess,
135    NewExpression,
136    TupleExpression,
137    UnaryOperation,
138
139    // Statements
140    Block,
141    Break,
142    Continue,
143    DoWhileStatement,
144    EmitStatement,
145    ExpressionStatement,
146    ForStatement,
147    IfStatement,
148    InlineAssembly,
149    PlaceholderStatement,
150    Return,
151    RevertStatement,
152    TryStatement,
153    UncheckedBlock,
154    VariableDeclarationStatement,
155    VariableDeclaration,
156    WhileStatement,
157
158    // Yul statements
159    YulAssignment,
160    YulBlock,
161    YulBreak,
162    YulCase,
163    YulContinue,
164    YulExpressionStatement,
165    YulLeave,
166    YulForLoop,
167    YulFunctionDefinition,
168    YulIf,
169    YulSwitch,
170    YulVariableDeclaration,
171
172    // Yul expressions
173    YulFunctionCall,
174    YulIdentifier,
175    YulLiteral,
176
177    // Yul literals
178    YulLiteralValue,
179    YulHexValue,
180    YulTypedName,
181
182    // Definitions
183    ContractDefinition,
184    FunctionDefinition,
185    EventDefinition,
186    ErrorDefinition,
187    ModifierDefinition,
188    StructDefinition,
189    EnumDefinition,
190    UserDefinedValueTypeDefinition,
191
192    // Directives
193    PragmaDirective,
194    ImportDirective,
195    UsingForDirective,
196
197    // Misc
198    SourceUnit,
199    InheritanceSpecifier,
200    ElementaryTypeName,
201    FunctionTypeName,
202    ParameterList,
203    TryCatchClause,
204    ModifierInvocation,
205    UserDefinedTypeName,
206    ArrayTypeName,
207    Mapping,
208
209    /// An unknown AST node type.
210    Other(String),
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn can_parse_ast() {
219        let ast = include_str!("../../../../../test-data/ast/ast-erc4626.json");
220        let _ast: Ast = serde_json::from_str(ast).unwrap();
221    }
222}