mod macros;
mod misc;
pub use misc::*;
pub mod util;
pub mod visitor;
pub(crate) mod lowfidelity;
pub use lowfidelity::{Ast, Node, NodeType, SourceLocation as LowFidelitySourceLocation};
pub mod yul;
use crate::artifacts::serde_helpers;
use macros::{ast_node, expr_node, node_group, stmt_node};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use yul::YulBlock;
ast_node!(
struct SourceUnit {
#[serde(rename = "absolutePath")]
absolute_path: String,
#[serde(default, rename = "exportedSymbols")]
exported_symbols: BTreeMap<String, Vec<usize>>,
#[serde(default)]
license: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
nodes: Vec<SourceUnitPart>,
}
);
node_group! {
SourceUnitPart;
PragmaDirective,
ImportDirective,
UsingForDirective,
VariableDeclaration,
EnumDefinition,
ErrorDefinition,
FunctionDefinition,
StructDefinition,
UserDefinedValueTypeDefinition,
ContractDefinition,
}
node_group! {
Expression;
Assignment,
BinaryOperation,
Conditional,
ElementaryTypeNameExpression,
FunctionCall,
FunctionCallOptions,
Identifier,
IndexAccess,
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,
}
node_group! {
Statement;
Block,
Break,
Continue,
DoWhileStatement,
EmitStatement,
ExpressionStatement,
ForStatement,
IfStatement,
InlineAssembly,
PlaceholderStatement,
Return,
RevertStatement,
TryStatement,
UncheckedBlock,
VariableDeclarationStatement,
WhileStatement,
}
node_group! {
ContractDefinitionPart;
EnumDefinition,
ErrorDefinition,
EventDefinition,
FunctionDefinition,
ModifierDefinition,
StructDefinition,
UserDefinedValueTypeDefinition,
UsingForDirective,
VariableDeclaration,
}
node_group! {
TypeName;
ArrayTypeName,
ElementaryTypeName,
FunctionTypeName,
Mapping,
UserDefinedTypeName,
}
node_group! {
UserDefinedTypeNameOrIdentifierPath;
UserDefinedTypeName,
IdentifierPath,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BlockOrStatement {
Statement(Statement),
Block(Block),
}
node_group! {
ExpressionOrVariableDeclarationStatement;
ExpressionStatement,
VariableDeclarationStatement
}
node_group! {
IdentifierOrIdentifierPath;
Identifier,
IdentifierPath
}
ast_node!(
struct ContractDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
#[serde(default, rename = "abstract")]
is_abstract: bool,
base_contracts: Vec<InheritanceSpecifier>,
canonical_name: Option<String>,
contract_dependencies: Vec<usize>,
#[serde(rename = "contractKind")]
kind: ContractKind,
documentation: Option<StructuredDocumentation>,
fully_implemented: bool,
linearized_base_contracts: Vec<usize>,
nodes: Vec<ContractDefinitionPart>,
scope: usize,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
used_errors: Vec<usize>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
used_events: Vec<usize>,
#[serde(default, rename = "internalFunctionIDs")]
internal_function_ids: BTreeMap<usize, usize>,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ContractKind {
Contract,
Interface,
Library,
}
ast_node!(
struct InheritanceSpecifier {
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
arguments: Vec<Expression>,
base_name: UserDefinedTypeNameOrIdentifierPath,
}
);
expr_node!(
struct Assignment {
#[serde(rename = "leftHandSide")]
lhs: Expression,
operator: AssignmentOperator,
#[serde(rename = "rightHandSide")]
rhs: Expression,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AssignmentOperator {
#[serde(rename = "=")]
Assign,
#[serde(rename = "+=")]
AddAssign,
#[serde(rename = "-=")]
SubAssign,
#[serde(rename = "*=")]
MulAssign,
#[serde(rename = "/=")]
DivAssign,
#[serde(rename = "%=")]
ModAssign,
#[serde(rename = "|=")]
OrAssign,
#[serde(rename = "&=")]
AndAssign,
#[serde(rename = "^=")]
XorAssign,
#[serde(rename = ">>=")]
ShrAssign,
#[serde(rename = "<<=")]
ShlAssign,
}
ast_node!(
struct BinaryOperation {
common_type: TypeDescriptions,
#[serde(rename = "leftExpression")]
lhs: Expression,
operator: BinaryOperator,
#[serde(rename = "rightExpression")]
rhs: Expression,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOperator {
#[serde(rename = "+")]
Add,
#[serde(rename = "-")]
Sub,
#[serde(rename = "*")]
Mul,
#[serde(rename = "/")]
Div,
#[serde(rename = "%")]
Mod,
#[serde(rename = "**")]
Pow,
#[serde(rename = "&&")]
And,
#[serde(rename = "||")]
Or,
#[serde(rename = "!=")]
NotEqual,
#[serde(rename = "==")]
Equal,
#[serde(rename = "<")]
LessThan,
#[serde(rename = "<=")]
LessThanOrEqual,
#[serde(rename = ">")]
GreaterThan,
#[serde(rename = ">=")]
GreaterThanOrEqual,
#[serde(rename = "^")]
Xor,
#[serde(rename = "~")]
BitNot,
#[serde(rename = "&")]
BitAnd,
#[serde(rename = "|")]
BitOr,
#[serde(rename = "<<")]
Shl,
#[serde(rename = ">>")]
Shr,
}
expr_node!(
struct Conditional {
condition: Expression,
false_expression: Expression,
true_expression: Expression,
}
);
expr_node!(
struct ElementaryTypeNameExpression {
type_name: ElementaryOrRawTypeName,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ElementaryOrRawTypeName {
ElementaryTypeName(ElementaryTypeName),
Raw(String),
}
ast_node!(
struct ElementaryTypeName {
type_descriptions: TypeDescriptions,
name: String,
state_mutability: Option<StateMutability>,
}
);
expr_node!(
struct FunctionCall {
arguments: Vec<Expression>,
expression: Expression,
kind: FunctionCallKind,
names: Vec<String>,
#[serde(default)]
try_call: bool,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FunctionCallKind {
FunctionCall,
TypeConversion,
StructConstructorCall,
}
expr_node!(
struct FunctionCallOptions {
expression: Expression,
names: Vec<String>,
options: Vec<Expression>,
}
);
ast_node!(
struct Identifier {
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
argument_types: Vec<TypeDescriptions>,
name: String,
overloaded_declarations: Vec<isize>,
referenced_declaration: Option<isize>,
type_descriptions: TypeDescriptions,
}
);
expr_node!(
struct IndexAccess {
base_expression: Expression,
index_expression: Option<Expression>,
}
);
expr_node!(
struct IndexRangeAccess {
base_expression: Expression,
start_expression: Option<Expression>,
end_expression: Option<Expression>,
}
);
expr_node!(
struct Literal {
hex_value: String,
kind: LiteralKind,
subdenomination: Option<String>, value: Option<String>, }
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum LiteralKind {
Bool,
Number,
String,
HexString,
UnicodeString,
}
expr_node!(
struct MemberAccess {
expression: Expression,
member_name: String,
referenced_declaration: Option<isize>,
}
);
expr_node!(
struct NewExpression {
type_name: TypeName,
}
);
ast_node!(
struct ArrayTypeName {
type_descriptions: TypeDescriptions,
base_type: TypeName,
length: Option<Expression>,
}
);
ast_node!(
struct FunctionTypeName {
type_descriptions: TypeDescriptions,
parameter_types: ParameterList,
return_parameter_types: ParameterList,
state_mutability: StateMutability,
visibility: Visibility,
}
);
ast_node!(
struct ParameterList {
parameters: Vec<VariableDeclaration>,
}
);
ast_node!(
struct VariableDeclaration {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
base_functions: Vec<usize>,
#[serde(default)]
constant: bool,
#[serde(default)]
state_variable: bool,
documentation: Option<StructuredDocumentation>,
function_selector: Option<String>, #[serde(default)]
indexed: bool,
#[serde(default)]
mutability: Option<Mutability>,
overrides: Option<OverrideSpecifier>,
scope: usize,
storage_location: StorageLocation,
type_descriptions: TypeDescriptions,
type_name: Option<TypeName>,
value: Option<Expression>,
visibility: Visibility,
}
);
impl VariableDeclaration {
pub fn mutability(&self) -> &Mutability {
if let Some(mutability) = &self.mutability {
mutability
} else if self.constant {
&Mutability::Constant
} else if self.state_variable {
&Mutability::Mutable
} else {
unreachable!()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StructuredDocumentation {
Parsed { text: String },
Text(String),
}
ast_node!(
struct OverrideSpecifier {
overrides: Vec<UserDefinedTypeNameOrIdentifierPath>,
}
);
ast_node!(
struct UserDefinedTypeName {
type_descriptions: TypeDescriptions,
contract_scope: Option<String>, name: Option<String>,
path_node: Option<IdentifierPath>,
referenced_declaration: isize,
}
);
ast_node!(
struct IdentifierPath {
name: String,
referenced_declaration: isize,
}
);
ast_node!(
struct Mapping {
type_descriptions: TypeDescriptions,
key_type: TypeName,
value_type: TypeName,
}
);
expr_node!(
struct TupleExpression {
components: Vec<Option<Expression>>,
is_inline_array: bool,
}
);
expr_node!(
struct UnaryOperation {
operator: UnaryOperator,
prefix: bool,
sub_expression: Expression,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOperator {
#[serde(rename = "++")]
Increment,
#[serde(rename = "--")]
Decrement,
#[serde(rename = "-")]
Negate,
#[serde(rename = "!")]
Not,
#[serde(rename = "~")]
BitNot,
#[serde(rename = "delete")]
Delete,
}
ast_node!(
struct EnumDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
canonical_name: String,
members: Vec<EnumValue>,
}
);
ast_node!(
struct EnumValue {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
}
);
ast_node!(
struct ErrorDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
documentation: Option<StructuredDocumentation>,
error_selector: Option<String>, parameters: ParameterList,
}
);
ast_node!(
struct EventDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
anonymous: bool,
event_selector: Option<String>, documentation: Option<StructuredDocumentation>,
parameters: ParameterList,
}
);
ast_node!(
struct FunctionDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
base_functions: Vec<usize>,
body: Option<Block>,
documentation: Option<StructuredDocumentation>,
function_selector: Option<String>, implemented: bool,
modifiers: Vec<ModifierInvocation>,
overrides: Option<OverrideSpecifier>,
parameters: ParameterList,
return_parameters: ParameterList,
scope: usize,
visibility: Visibility,
kind: Option<FunctionKind>,
#[serde(default)]
state_mutability: Option<StateMutability>,
#[serde(default, rename = "virtual")]
is_virtual: bool,
#[serde(default)]
is_constructor: bool,
#[serde(default)]
is_declared_const: bool,
#[serde(default)]
is_payable: bool,
}
);
impl FunctionDefinition {
pub fn kind(&self) -> &FunctionKind {
if let Some(kind) = &self.kind {
kind
} else if self.is_constructor {
&FunctionKind::Constructor
} else {
&FunctionKind::Function
}
}
pub fn state_mutability(&self) -> &StateMutability {
if let Some(state_mutability) = &self.state_mutability {
state_mutability
} else if self.is_declared_const {
&StateMutability::View
} else if self.is_payable {
&StateMutability::Payable
} else {
&StateMutability::Nonpayable
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FunctionKind {
Function,
Receive,
Constructor,
Fallback,
FreeFunction,
}
ast_node!(
struct Block {
documentation: Option<String>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
statements: Vec<Statement>,
}
);
stmt_node!(
struct Break {}
);
stmt_node!(
struct Continue {}
);
stmt_node!(
struct DoWhileStatement {
block: Block,
condition: Expression,
}
);
stmt_node!(
struct EmitStatement {
event_call: FunctionCall,
}
);
stmt_node!(
struct ExpressionStatement {
expression: Expression,
}
);
stmt_node!(
struct ForStatement {
body: BlockOrStatement,
condition: Option<Expression>,
initialization_expression: Option<ExpressionOrVariableDeclarationStatement>,
loop_expression: Option<ExpressionStatement>,
}
);
stmt_node!(
struct VariableDeclarationStatement {
assignments: Vec<Option<usize>>,
declarations: Vec<Option<VariableDeclaration>>,
initial_value: Option<Expression>,
}
);
stmt_node!(
struct IfStatement {
condition: Expression,
false_body: Option<BlockOrStatement>,
true_body: BlockOrStatement,
}
);
ast_node!(
struct InlineAssembly {
documentation: Option<String>,
#[serde(rename = "AST")]
ast: YulBlock,
external_references: Vec<ExternalInlineAssemblyReference>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
flags: Vec<InlineAssemblyFlag>,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalInlineAssemblyReference {
#[serde(with = "serde_helpers::display_from_str")]
pub src: SourceLocation,
pub declaration: usize,
#[serde(default)]
pub offset: bool,
#[serde(default)]
pub slot: bool,
#[serde(default)]
pub length: bool,
pub value_size: usize,
pub suffix: Option<AssemblyReferenceSuffix>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AssemblyReferenceSuffix {
Slot,
Offset,
Length,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum InlineAssemblyFlag {
MemorySafe,
}
stmt_node!(
struct PlaceholderStatement {}
);
stmt_node!(
struct Return {
expression: Option<Expression>,
function_return_parameters: usize,
}
);
stmt_node!(
struct RevertStatement {
error_call: FunctionCall,
}
);
stmt_node!(
struct TryStatement {
clauses: Vec<TryCatchClause>,
external_call: FunctionCall,
}
);
ast_node!(
struct TryCatchClause {
block: Block,
error_name: String,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
parameters: Vec<ParameterList>,
}
);
stmt_node!(
struct UncheckedBlock {
statements: Vec<Statement>,
}
);
stmt_node!(
struct WhileStatement {
body: BlockOrStatement,
condition: Expression,
}
);
ast_node!(
struct ModifierInvocation {
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
arguments: Vec<Expression>,
kind: Option<ModifierInvocationKind>,
modifier_name: IdentifierOrIdentifierPath,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ModifierInvocationKind {
ModifierInvocation,
BaseConstructorSpecifier,
}
ast_node!(
struct ModifierDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
base_modifiers: Vec<usize>,
body: Block,
documentation: Option<StructuredDocumentation>,
overrides: Option<OverrideSpecifier>,
parameters: ParameterList,
#[serde(default, rename = "virtual")]
is_virtual: bool,
visibility: Visibility,
}
);
ast_node!(
struct StructDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
canonical_name: String,
members: Vec<VariableDeclaration>,
scope: usize,
visibility: Visibility,
}
);
ast_node!(
struct UserDefinedValueTypeDefinition {
name: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
canonical_name: Option<String>,
underlying_type: TypeName,
}
);
ast_node!(
struct UsingForDirective {
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
function_list: Vec<FunctionIdentifierPath>,
#[serde(default)]
global: bool,
library_name: Option<UserDefinedTypeNameOrIdentifierPath>,
type_name: Option<TypeName>,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FunctionIdentifierPath {
pub function: IdentifierPath,
}
ast_node!(
struct ImportDirective {
absolute_path: String,
file: String,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
name_location: Option<SourceLocation>,
scope: usize,
source_unit: usize,
symbol_aliases: Vec<SymbolAlias>,
unit_alias: String,
}
);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SymbolAlias {
pub foreign: Identifier,
pub local: Option<String>,
#[serde(default, with = "serde_helpers::display_from_str_opt")]
pub name_location: Option<SourceLocation>,
}
ast_node!(
struct PragmaDirective {
literals: Vec<String>,
}
);
#[cfg(test)]
mod tests {
use super::*;
use std::{fs, path::PathBuf};
#[test]
fn can_parse_ast() {
fs::read_dir(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data").join("ast"))
.unwrap()
.for_each(|path| {
let path = path.unwrap().path();
let path_str = path.to_string_lossy();
let input = fs::read_to_string(&path).unwrap();
let deserializer = &mut serde_json::Deserializer::from_str(&input);
let result: Result<SourceUnit, _> = serde_path_to_error::deserialize(deserializer);
match result {
Err(e) => {
println!("... {path_str} fail: {e}");
panic!();
}
Ok(_) => {
println!("... {path_str} ok");
}
}
})
}
}