Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat: remaining AST nodes #1201

Merged
merged 5 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 99 additions & 106 deletions ethers-solc/src/artifacts/ast.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Bindings for solc's `ast` output field

use crate::artifacts::serde_helpers;
use serde::{Deserialize, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr};

/// Represents the AST field in the solc output
Expand All @@ -18,24 +18,47 @@ pub struct Ast {
pub src: SourceLocation,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nodes: Vec<Node>,

/// Node attributes that were not deserialized.
#[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Node {
/// The node ID.
pub id: usize,

/// The node type.
#[serde(rename = "nodeType")]
pub node_type: NodeType,

/// The location of the node in the source file.
#[serde(with = "serde_helpers::display_from_str")]
pub src: SourceLocation,

/// Child nodes for some node types.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nodes: Vec<Node>,

/// Body node for some node types.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub body: Option<Box<Node>>,

/// Node attributes that were not deserialized.
#[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>,
}

/// Represents the source location of a node : `<start>:<length>:<index>`
impl Node {
/// Deserialize a serialized node attribute.
pub fn attribute<D: DeserializeOwned>(&self, key: impl AsRef<str>) -> Option<D> {
// TODO: Can we avoid this clone?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't avoid because from value consumes the Value

self.other.get(key.as_ref()).and_then(|v| serde_json::from_value(v.clone()).ok())
Comment on lines +56 to +57
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably improved with Serde's tagged enum by doing enum NodeType { FunctionCall { foo: u8 } } or whatever, but nbd now

}
}

/// Represents the source location of a node: `<start byte>:<length>:<source index>`.
///
/// The `length` and `index` can be -1 which is represented as `None`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -95,20 +118,89 @@ impl fmt::Display for SourceLocation {
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum NodeType {
// Expressions
Assignment,
BinaryOperation,
Conditional,
ElementaryTypeNameExpression,
FunctionCall,
FunctionCallOptions,
Identifier,
IndexAccess,
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,

// Statements
Block,
Break,
Continue,
DoWhileStatement,
EmitStatement,
ExpressionStatement,
ForStatement,
IfStatement,
InlineAssembly,
PlaceholderStatement,
Return,
RevertStatement,
TryStatement,
UncheckedBlock,
VariableDeclarationStatement,
VariableDeclaration,
WhileStatement,

// Yul statements
YulAssignment,
YulBlock,
YulBreak,
YulContinue,
YulExpressionStatement,
YulLeave,
YulForLoop,
YulFunctionDefinition,
YulIf,
YulSwitch,
YulVariableDeclaration,
YulFunctionDefinition,
SourceUnit,
PragmaDirective,

// Yul expressions
YulFunctionCall,
YulIdentifier,
YulLiteral,

// Yul literals
YulLiteralValue,
YulHexValue,

// Definitions
ContractDefinition,
FunctionDefinition,
EventDefinition,
ErrorDefinition,
ModifierDefinition,
StructDefinition,
EnumDefinition,
UserDefinedValueTypeDefinition,

// Directives
PragmaDirective,
ImportDirective,
UsingForDirective,

// Misc
SourceUnit,
InheritanceSpecifier,
ElementaryTypeName,
FunctionTypeName,
ParameterList,
TryCatchClause,
ModifierInvocation,

/// An unknown AST node type.
Other(String),
}

Expand All @@ -118,106 +210,7 @@ mod tests {

#[test]
fn can_parse_ast() {
let ast = r#"
{
"absolutePath": "input.sol",
"exportedSymbols":
{
"Ballot":
[
2
],
"Ballot2":
[
3
],
"Ballot3":
[
4
]
},
"id": 5,
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"solidity",
">=",
"0.4",
".0"
],
"nodeType": "PragmaDirective",
"src": "1:24:0"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "Ballot",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 2,
"linearizedBaseContracts":
[
2
],
"name": "Ballot",
"nameLocation": "36:6:0",
"nodeType": "ContractDefinition",
"nodes": [],
"scope": 5,
"src": "27:20:0",
"usedErrors": []
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "Ballot2",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 3,
"linearizedBaseContracts":
[
3
],
"name": "Ballot2",
"nameLocation": "58:7:0",
"nodeType": "ContractDefinition",
"nodes": [],
"scope": 5,
"src": "49:21:0",
"usedErrors": []
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "Ballot3",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 4,
"linearizedBaseContracts":
[
4
],
"name": "Ballot3",
"nameLocation": "81:7:0",
"nodeType": "ContractDefinition",
"nodes": [],
"scope": 5,
"src": "72:21:0",
"usedErrors": []
}
],
"src": "1:92:0"
}
"#;
let ast = include_str!("../../test-data/ast/ast-erc4626.json");
let _ast: Ast = serde_json::from_str(ast).unwrap();

dbg!(serde_json::from_str::<serde_json::Value>("{}").unwrap());
}
}
Loading