feat: remaining AST nodes (#1201)

* feat: remaining AST nodes

* docs: few rustdocs

* feat: add more node fields

* feat: add more ast nodes

* fix: actually deserialize node type

Note: This breaks `NodeType::Other` :/
This commit is contained in:
Bjerg 2022-05-10 19:43:22 +02:00 committed by GitHub
parent ab8ec1f75b
commit 0a031417d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 6712 additions and 107 deletions

View File

@ -1,7 +1,7 @@
//! Bindings for solc's `ast` output field //! Bindings for solc's `ast` output field
use crate::artifacts::serde_helpers; use crate::artifacts::serde_helpers;
use serde::{Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr}; use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr};
/// Represents the AST field in the solc output /// Represents the AST field in the solc output
@ -18,24 +18,47 @@ pub struct Ast {
pub src: SourceLocation, pub src: SourceLocation,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nodes: Vec<Node>, pub nodes: Vec<Node>,
/// Node attributes that were not deserialized.
#[serde(flatten)] #[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>, pub other: BTreeMap<String, serde_json::Value>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Node { pub struct Node {
/// The node ID.
pub id: usize, pub id: usize,
/// The node type.
#[serde(rename = "nodeType")] #[serde(rename = "nodeType")]
pub node_type: NodeType, pub node_type: NodeType,
/// The location of the node in the source file.
#[serde(with = "serde_helpers::display_from_str")] #[serde(with = "serde_helpers::display_from_str")]
pub src: SourceLocation, pub src: SourceLocation,
/// Child nodes for some node types.
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nodes: Vec<Node>, 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)] #[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>, 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?
self.other.get(key.as_ref()).and_then(|v| serde_json::from_value(v.clone()).ok())
}
}
/// Represents the source location of a node: `<start byte>:<length>:<source index>`.
/// ///
/// The `length` and `index` can be -1 which is represented as `None` /// The `length` and `index` can be -1 which is represented as `None`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -95,20 +118,89 @@ impl fmt::Display for SourceLocation {
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum NodeType { 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, YulAssignment,
YulBlock, YulBlock,
YulBreak,
YulContinue,
YulExpressionStatement, YulExpressionStatement,
YulLeave,
YulForLoop, YulForLoop,
YulIf,
YulVariableDeclaration,
YulFunctionDefinition, YulFunctionDefinition,
SourceUnit, YulIf,
PragmaDirective, YulSwitch,
YulVariableDeclaration,
// Yul expressions
YulFunctionCall,
YulIdentifier,
YulLiteral,
// Yul literals
YulLiteralValue,
YulHexValue,
// Definitions
ContractDefinition, ContractDefinition,
FunctionDefinition,
EventDefinition, EventDefinition,
ErrorDefinition, ErrorDefinition,
ModifierDefinition,
StructDefinition,
EnumDefinition,
UserDefinedValueTypeDefinition,
// Directives
PragmaDirective,
ImportDirective,
UsingForDirective,
// Misc
SourceUnit,
InheritanceSpecifier,
ElementaryTypeName,
FunctionTypeName,
ParameterList,
TryCatchClause,
ModifierInvocation,
/// An unknown AST node type.
Other(String), Other(String),
} }
@ -118,106 +210,7 @@ mod tests {
#[test] #[test]
fn can_parse_ast() { fn can_parse_ast() {
let ast = r#" let ast = include_str!("../../test-data/ast/ast-erc4626.json");
{
"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: Ast = serde_json::from_str(ast).unwrap(); let _ast: Ast = serde_json::from_str(ast).unwrap();
dbg!(serde_json::from_str::<serde_json::Value>("{}").unwrap());
} }
} }

File diff suppressed because it is too large Load Diff