1102 lines
28 KiB
Rust
1102 lines
28 KiB
Rust
//! Bindings for the Solidity and Yul ASTs.
|
|
//!
|
|
//! The Yul AST bindings are available in the [yul] module.
|
|
//!
|
|
//! To gain an overview of the AST, it might be helpful to start at the entry point of a complete
|
|
//! Solidity AST: the [SourceUnit] node.
|
|
//!
|
|
//! # Version Support
|
|
//!
|
|
//! These types should be compatible with at least Solidity 0.5.x and above, but may also support
|
|
//! 0.4.x-0.5.x in most cases.
|
|
//!
|
|
//! The legacy Solidity AST is not supported.
|
|
mod macros;
|
|
mod misc;
|
|
pub use misc::*;
|
|
pub mod util;
|
|
pub mod visitor;
|
|
|
|
/// A low fidelity representation of the AST.
|
|
pub(crate) mod lowfidelity;
|
|
pub use lowfidelity::{Ast, Node, NodeType, SourceLocation as LowFidelitySourceLocation};
|
|
|
|
/// Types for the Yul AST.
|
|
///
|
|
/// The Yul AST is embedded into the Solidity AST for inline assembly blocks.
|
|
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!(
|
|
/// The root node of a Solidity AST.
|
|
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,
|
|
}
|
|
|
|
// TODO: Better name
|
|
node_group! {
|
|
UserDefinedTypeNameOrIdentifierPath;
|
|
|
|
UserDefinedTypeName,
|
|
IdentifierPath,
|
|
}
|
|
|
|
// TODO: Better name
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum BlockOrStatement {
|
|
Statement(Statement),
|
|
Block(Block),
|
|
}
|
|
|
|
// TODO: Better name
|
|
node_group! {
|
|
ExpressionOrVariableDeclarationStatement;
|
|
|
|
ExpressionStatement,
|
|
VariableDeclarationStatement
|
|
}
|
|
|
|
// TODO: Better name
|
|
node_group! {
|
|
IdentifierOrIdentifierPath;
|
|
|
|
Identifier,
|
|
IdentifierPath
|
|
}
|
|
|
|
ast_node!(
|
|
/// A contract definition.
|
|
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)]
|
|
used_errors: Vec<usize>,
|
|
}
|
|
);
|
|
|
|
/// All Solidity contract kinds.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum ContractKind {
|
|
/// A normal contract.
|
|
Contract,
|
|
/// An interface.
|
|
Interface,
|
|
/// A library.
|
|
Library,
|
|
}
|
|
|
|
ast_node!(
|
|
/// An inheritance specifier.
|
|
struct InheritanceSpecifier {
|
|
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
|
|
arguments: Vec<Expression>,
|
|
base_name: UserDefinedTypeNameOrIdentifierPath,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// An assignment expression.
|
|
struct Assignment {
|
|
#[serde(rename = "leftHandSide")]
|
|
lhs: Expression,
|
|
operator: AssignmentOperator,
|
|
#[serde(rename = "rightHandSide")]
|
|
rhs: Expression,
|
|
}
|
|
);
|
|
|
|
/// Assignment operators.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum AssignmentOperator {
|
|
/// Simple assignment (`=`)
|
|
#[serde(rename = "=")]
|
|
Assign,
|
|
/// Add and assign (`+=`)
|
|
#[serde(rename = "+=")]
|
|
AddAssign,
|
|
/// Subtract and assign (`-=`)
|
|
#[serde(rename = "-=")]
|
|
SubAssign,
|
|
/// Multiply and assign (`*=`)
|
|
#[serde(rename = "*=")]
|
|
MulAssign,
|
|
/// Divide and assign (`/=`)
|
|
#[serde(rename = "/=")]
|
|
DivAssign,
|
|
/// Modulo and assign (`%=`)
|
|
#[serde(rename = "%=")]
|
|
ModAssign,
|
|
/// Bitwise or and assign (`|=`)
|
|
#[serde(rename = "|=")]
|
|
OrAssign,
|
|
/// Bitwise and and assign (`&=`)
|
|
#[serde(rename = "&=")]
|
|
AndAssign,
|
|
/// Bitwise xor and assign (`^=`)
|
|
#[serde(rename = "^=")]
|
|
XorAssign,
|
|
/// Right shift and assign (`>>=`)
|
|
#[serde(rename = ">>=")]
|
|
ShrAssign,
|
|
/// Left shift and assign (`<<=`)
|
|
#[serde(rename = "<<=")]
|
|
ShlAssign,
|
|
}
|
|
|
|
ast_node!(
|
|
/// A binary operation.
|
|
struct BinaryOperation {
|
|
common_type: TypeDescriptions,
|
|
#[serde(rename = "leftExpression")]
|
|
lhs: Expression,
|
|
operator: BinaryOperator,
|
|
#[serde(rename = "rightExpression")]
|
|
rhs: Expression,
|
|
}
|
|
);
|
|
|
|
/// Binary operators.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum BinaryOperator {
|
|
/// Addition (`+`)
|
|
#[serde(rename = "+")]
|
|
Add,
|
|
/// Subtraction (`-`)
|
|
#[serde(rename = "-")]
|
|
Sub,
|
|
/// Multiplication (`*`)
|
|
#[serde(rename = "*")]
|
|
Mul,
|
|
/// Division (`/`)
|
|
#[serde(rename = "/")]
|
|
Div,
|
|
/// Modulo (`%`)
|
|
#[serde(rename = "%")]
|
|
Mod,
|
|
/// Exponentiation (`**`)
|
|
#[serde(rename = "**")]
|
|
Pow,
|
|
/// Logical and (`&&`)
|
|
#[serde(rename = "&&")]
|
|
And,
|
|
/// Logical or (`||`)
|
|
#[serde(rename = "||")]
|
|
Or,
|
|
/// Not equals (`!=`)
|
|
#[serde(rename = "!=")]
|
|
NotEqual,
|
|
/// Equals (`==`)
|
|
#[serde(rename = "==")]
|
|
Equal,
|
|
/// Less than (`<`)
|
|
#[serde(rename = "<")]
|
|
LessThan,
|
|
/// Less than or equal (`<=`)
|
|
#[serde(rename = "<=")]
|
|
LessThanOrEqual,
|
|
/// Greater than (`>`)
|
|
#[serde(rename = ">")]
|
|
GreaterThan,
|
|
/// Greater than or equal (`>=`)
|
|
#[serde(rename = ">=")]
|
|
GreaterThanOrEqual,
|
|
/// Bitwise xor (`^`)
|
|
#[serde(rename = "^")]
|
|
Xor,
|
|
/// Bitwise not (`~`)
|
|
#[serde(rename = "~")]
|
|
BitNot,
|
|
/// Bitwise and (`&`)
|
|
#[serde(rename = "&")]
|
|
BitAnd,
|
|
/// Bitwise or (`|`)
|
|
#[serde(rename = "|")]
|
|
BitOr,
|
|
/// Shift left (`<<`)
|
|
#[serde(rename = "<<")]
|
|
Shl,
|
|
/// Shift right (`>>`)
|
|
#[serde(rename = ">>")]
|
|
Shr,
|
|
}
|
|
|
|
expr_node!(
|
|
/// A conditional expression.
|
|
struct Conditional {
|
|
/// The condition.
|
|
condition: Expression,
|
|
/// The expression to evaluate if falsy.
|
|
false_expression: Expression,
|
|
/// The expression to evaluate if truthy.
|
|
true_expression: Expression,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
struct ElementaryTypeNameExpression {
|
|
type_name: ElementaryOrRawTypeName,
|
|
}
|
|
);
|
|
|
|
// TODO: Better name
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum ElementaryOrRawTypeName {
|
|
/// An [ElementaryTypeName] node that describes the type.
|
|
///
|
|
/// This variant applies to newer compiler versions.
|
|
ElementaryTypeName(ElementaryTypeName),
|
|
/// A string representing the type name.
|
|
///
|
|
/// This variant applies to older compiler versions.
|
|
Raw(String),
|
|
}
|
|
|
|
ast_node!(
|
|
struct ElementaryTypeName {
|
|
type_descriptions: TypeDescriptions,
|
|
name: String,
|
|
state_mutability: Option<StateMutability>,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// A function call expression.
|
|
struct FunctionCall {
|
|
arguments: Vec<Expression>,
|
|
expression: Expression,
|
|
kind: FunctionCallKind,
|
|
names: Vec<String>,
|
|
#[serde(default)]
|
|
try_call: bool,
|
|
}
|
|
);
|
|
|
|
/// Function call kinds.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum FunctionCallKind {
|
|
/// A regular function call.
|
|
FunctionCall,
|
|
/// A type conversion (e.g. `bytes(x)`).
|
|
TypeConversion,
|
|
/// A struct constructor call (e.g. `MyStruct({ ... })`).
|
|
StructConstructorCall,
|
|
}
|
|
|
|
expr_node!(
|
|
/// A function call options expression (e.g. `x.f{gas: 1}`).
|
|
struct FunctionCallOptions {
|
|
expression: Expression,
|
|
names: Vec<String>,
|
|
options: Vec<Expression>,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// An identifier.
|
|
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!(
|
|
/// An index access.
|
|
struct IndexAccess {
|
|
base_expression: Expression,
|
|
index_expression: Option<Expression>,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// An index range access.
|
|
struct IndexRangeAccess {
|
|
base_expression: Expression,
|
|
start_expression: Option<Expression>,
|
|
end_expression: Option<Expression>,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// A literal value.
|
|
struct Literal {
|
|
// TODO
|
|
hex_value: String,
|
|
kind: LiteralKind,
|
|
subdenomination: Option<String>, // TODO
|
|
value: Option<String>, // TODO
|
|
}
|
|
);
|
|
|
|
/// Literal kinds.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum LiteralKind {
|
|
/// A boolean.
|
|
Bool,
|
|
/// A number.
|
|
Number,
|
|
/// A string.
|
|
String,
|
|
/// A hexadecimal string.
|
|
HexString,
|
|
/// A unicode string.
|
|
UnicodeString,
|
|
}
|
|
|
|
expr_node!(
|
|
/// Member access.
|
|
struct MemberAccess {
|
|
expression: Expression,
|
|
member_name: String,
|
|
referenced_declaration: Option<isize>,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// A `new` expression.
|
|
struct NewExpression {
|
|
type_name: TypeName,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// An array type name.
|
|
struct ArrayTypeName {
|
|
type_descriptions: TypeDescriptions,
|
|
base_type: TypeName,
|
|
length: Option<Expression>,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A function type name.
|
|
struct FunctionTypeName {
|
|
type_descriptions: TypeDescriptions,
|
|
parameter_types: ParameterList,
|
|
return_parameter_types: ParameterList,
|
|
state_mutability: StateMutability,
|
|
visibility: Visibility,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A parameter list.
|
|
struct ParameterList {
|
|
parameters: Vec<VariableDeclaration>,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A variable declaration.
|
|
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>,
|
|
/// Marks whether or not the variable is a constant before Solidity 0.7.x.
|
|
///
|
|
/// After 0.7.x you must use `mutability`. For cross-version compatibility use
|
|
/// [`VariableDeclaration::mutability()`].
|
|
#[serde(default)]
|
|
constant: bool,
|
|
/// Marks whether or not the variable is a state variable before Solidity 0.7.x.
|
|
///
|
|
/// After 0.7.x you must use `mutability`. For cross-version compatibility use
|
|
/// [`VariableDeclaration::mutability()`].
|
|
#[serde(default)]
|
|
state_variable: bool,
|
|
documentation: Option<StructuredDocumentation>,
|
|
function_selector: Option<String>, // TODO
|
|
#[serde(default)]
|
|
indexed: bool,
|
|
/// Marks the variable's mutability from Solidity 0.7.x onwards.
|
|
/// For cross-version compatibility use [`VariableDeclaration::mutability()`].
|
|
#[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 {
|
|
/// Returns the mutability of the variable that was declared.
|
|
///
|
|
/// This is a helper to check variable mutability across Solidity versions.
|
|
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!()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Structured documentation (NatSpec).
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
pub enum StructuredDocumentation {
|
|
/// The documentation is provided in the form of an AST node.
|
|
Parsed { text: String },
|
|
/// The documentation is provided in the form of a string literal.
|
|
Text(String),
|
|
}
|
|
|
|
ast_node!(
|
|
/// An override specifier.
|
|
struct OverrideSpecifier {
|
|
overrides: Vec<UserDefinedTypeNameOrIdentifierPath>,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A user defined type name.
|
|
struct UserDefinedTypeName {
|
|
type_descriptions: TypeDescriptions,
|
|
contract_scope: Option<String>, // TODO
|
|
name: Option<String>,
|
|
path_node: Option<IdentifierPath>,
|
|
referenced_declaration: isize,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// An identifier path.
|
|
struct IdentifierPath {
|
|
name: String,
|
|
referenced_declaration: isize,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A mapping type.
|
|
struct Mapping {
|
|
type_descriptions: TypeDescriptions,
|
|
key_type: TypeName,
|
|
value_type: TypeName,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// A tuple expression.
|
|
struct TupleExpression {
|
|
components: Vec<Expression>,
|
|
is_inline_array: bool,
|
|
}
|
|
);
|
|
|
|
expr_node!(
|
|
/// A unary operation.
|
|
struct UnaryOperation {
|
|
operator: UnaryOperator,
|
|
/// Whether the unary operator is before or after the expression (e.g. `x++` vs. `++x`)
|
|
prefix: bool,
|
|
sub_expression: Expression,
|
|
}
|
|
);
|
|
|
|
/// Unary operators.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum UnaryOperator {
|
|
/// Increment (`++`)
|
|
#[serde(rename = "++")]
|
|
Increment,
|
|
/// Decrement (`--`)
|
|
#[serde(rename = "--")]
|
|
Decrement,
|
|
/// Negate (`-`)
|
|
#[serde(rename = "-")]
|
|
Negate,
|
|
/// Not (`!`)
|
|
#[serde(rename = "!")]
|
|
Not,
|
|
/// Bitwise not (`~`)
|
|
#[serde(rename = "~")]
|
|
BitNot,
|
|
/// `delete`
|
|
#[serde(rename = "delete")]
|
|
Delete,
|
|
}
|
|
|
|
ast_node!(
|
|
/// An enum definition.
|
|
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!(
|
|
/// An enum value.
|
|
struct EnumValue {
|
|
name: String,
|
|
#[serde(default, with = "serde_helpers::display_from_str_opt")]
|
|
name_location: Option<SourceLocation>,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A custom error definition.
|
|
struct ErrorDefinition {
|
|
name: String,
|
|
#[serde(default, with = "serde_helpers::display_from_str_opt")]
|
|
name_location: Option<SourceLocation>,
|
|
documentation: Option<StructuredDocumentation>,
|
|
error_selector: Option<String>, // TODO
|
|
parameters: ParameterList,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// An event definition.
|
|
struct EventDefinition {
|
|
name: String,
|
|
#[serde(default, with = "serde_helpers::display_from_str_opt")]
|
|
name_location: Option<SourceLocation>,
|
|
anonymous: bool,
|
|
event_selector: Option<String>, // TODO
|
|
documentation: Option<StructuredDocumentation>,
|
|
parameters: ParameterList,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A function definition.
|
|
struct FunctionDefinition {
|
|
name: String,
|
|
#[serde(default, with = "serde_helpers::display_from_str_opt")]
|
|
name_location: Option<SourceLocation>,
|
|
#[serde(default)]
|
|
base_functions: Vec<usize>,
|
|
body: Option<Block>,
|
|
documentation: Option<StructuredDocumentation>,
|
|
function_selector: Option<String>, // TODO
|
|
implemented: bool,
|
|
modifiers: Vec<ModifierInvocation>,
|
|
overrides: Option<OverrideSpecifier>,
|
|
parameters: ParameterList,
|
|
return_parameters: ParameterList,
|
|
scope: usize,
|
|
visibility: Visibility,
|
|
/// The kind of function this node defines. Only valid for Solidity versions 0.5.x and
|
|
/// above.
|
|
///
|
|
/// For cross-version compatibility use [`FunctionDefinition::kind()`].
|
|
kind: Option<FunctionKind>,
|
|
/// The state mutability of the function.
|
|
///
|
|
/// Note: This was introduced in Solidity 0.5.x. For cross-version compatibility use
|
|
/// [`FunctionDefinition::state_mutability()`].
|
|
#[serde(default)]
|
|
state_mutability: Option<StateMutability>,
|
|
#[serde(default, rename = "virtual")]
|
|
is_virtual: bool,
|
|
/// Whether or not this function is the constructor. Only valid for Solidity versions below
|
|
/// 0.5.x.
|
|
///
|
|
/// After 0.5.x you must use `kind`. For cross-version compatibility use
|
|
/// [`FunctionDefinition::kind()`].
|
|
#[serde(default)]
|
|
is_constructor: bool,
|
|
/// Whether or not this function is constant (view or pure). Only valid for Solidity
|
|
/// versions below 0.5.x.
|
|
///
|
|
/// After 0.5.x you must use `state_mutability`. For cross-version compatibility use
|
|
/// [`FunctionDefinition::state_mutability()`].
|
|
#[serde(default)]
|
|
is_declared_const: bool,
|
|
/// Whether or not this function is payable. Only valid for Solidity versions below
|
|
/// 0.5.x.
|
|
///
|
|
/// After 0.5.x you must use `state_mutability`. For cross-version compatibility use
|
|
/// [`FunctionDefinition::state_mutability()`].
|
|
#[serde(default)]
|
|
is_payable: bool,
|
|
}
|
|
);
|
|
|
|
impl FunctionDefinition {
|
|
/// The kind of function this node defines.
|
|
pub fn kind(&self) -> &FunctionKind {
|
|
if let Some(kind) = &self.kind {
|
|
kind
|
|
} else if self.is_constructor {
|
|
&FunctionKind::Constructor
|
|
} else {
|
|
&FunctionKind::Function
|
|
}
|
|
}
|
|
|
|
/// The state mutability of the function.
|
|
///
|
|
/// Note: Before Solidity 0.5.x, this is an approximation, as there was no distinction between
|
|
/// `view` and `pure`.
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Function kinds.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum FunctionKind {
|
|
/// A contract function.
|
|
Function,
|
|
/// A receive function.
|
|
Receive,
|
|
/// A constructor.
|
|
Constructor,
|
|
/// A fallback function.
|
|
Fallback,
|
|
/// A free-standing function.
|
|
FreeFunction,
|
|
}
|
|
|
|
ast_node!(
|
|
/// A block of statements.
|
|
struct Block {
|
|
documentation: Option<String>,
|
|
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
|
|
statements: Vec<Statement>,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// The break keyword.
|
|
struct Break {}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// The continue keyword.
|
|
struct Continue {}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A do while statement.
|
|
struct DoWhileStatement {
|
|
block: Block,
|
|
condition: Expression,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// An emit statement.
|
|
struct EmitStatement {
|
|
event_call: FunctionCall,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// An expression statement.
|
|
struct ExpressionStatement {
|
|
expression: Expression,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A for statement.
|
|
struct ForStatement {
|
|
body: BlockOrStatement,
|
|
condition: Option<Expression>,
|
|
initialization_expression: Option<ExpressionOrVariableDeclarationStatement>,
|
|
loop_expression: Option<ExpressionStatement>,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A variable declaration statement.
|
|
struct VariableDeclarationStatement {
|
|
assignments: Vec<Option<usize>>,
|
|
declarations: Vec<Option<VariableDeclaration>>,
|
|
initial_value: Option<Expression>,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// An if statement.
|
|
struct IfStatement {
|
|
condition: Expression,
|
|
false_body: Option<BlockOrStatement>,
|
|
true_body: BlockOrStatement,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A block of inline assembly.
|
|
///
|
|
/// Refer to the [yul] module for Yul AST nodes.
|
|
struct InlineAssembly {
|
|
documentation: Option<String>,
|
|
#[serde(rename = "AST")]
|
|
ast: YulBlock,
|
|
// TODO: We need this camel case for the AST, but pascal case other places in ethers-solc
|
|
//evm_version: EvmVersion,
|
|
external_references: Vec<ExternalInlineAssemblyReference>,
|
|
#[serde(default)]
|
|
flags: Vec<InlineAssemblyFlag>,
|
|
}
|
|
);
|
|
|
|
/// A reference to an external variable or slot in an inline assembly block.
|
|
#[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>,
|
|
}
|
|
|
|
/// An assembly reference suffix.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum AssemblyReferenceSuffix {
|
|
/// The reference refers to a storage slot.
|
|
Slot,
|
|
/// The reference refers to an offset.
|
|
Offset,
|
|
/// The reference refers to a length.
|
|
Length,
|
|
}
|
|
|
|
/// Inline assembly flags.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum InlineAssemblyFlag {
|
|
MemorySafe,
|
|
}
|
|
|
|
stmt_node!(
|
|
/// A placeholder statement (`_`)
|
|
struct PlaceholderStatement {}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A return statement.
|
|
struct Return {
|
|
expression: Option<Expression>,
|
|
function_return_parameters: usize,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A revert statement.
|
|
struct RevertStatement {
|
|
error_call: FunctionCall,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A try/catch statement.
|
|
struct TryStatement {
|
|
clauses: Vec<TryCatchClause>,
|
|
external_call: FunctionCall,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A try/catch clause.
|
|
struct TryCatchClause {
|
|
block: Block,
|
|
error_name: String,
|
|
#[serde(default)]
|
|
parameters: Vec<ParameterList>,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// An unchecked block.
|
|
struct UncheckedBlock {
|
|
statements: Vec<Statement>,
|
|
}
|
|
);
|
|
|
|
stmt_node!(
|
|
/// A while statement.
|
|
struct WhileStatement {
|
|
body: BlockOrStatement,
|
|
condition: Expression,
|
|
}
|
|
);
|
|
|
|
ast_node!(
|
|
/// A modifier or base constructor invocation.
|
|
struct ModifierInvocation {
|
|
#[serde(default)]
|
|
arguments: Vec<Expression>,
|
|
kind: Option<ModifierInvocationKind>,
|
|
modifier_name: IdentifierOrIdentifierPath,
|
|
}
|
|
);
|
|
|
|
/// Modifier invocation kinds.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum ModifierInvocationKind {
|
|
/// A regular modifier invocation.
|
|
ModifierInvocation,
|
|
/// A base constructor invocation.
|
|
BaseConstructorSpecifier,
|
|
}
|
|
|
|
ast_node!(
|
|
/// A modifier definition.
|
|
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!(
|
|
/// A struct definition.
|
|
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!(
|
|
/// A user defined value type definition.
|
|
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!(
|
|
/// A using for directive.
|
|
struct UsingForDirective {
|
|
#[serde(default)]
|
|
function_list: Vec<FunctionIdentifierPath>,
|
|
#[serde(default)]
|
|
global: bool,
|
|
library_name: Option<UserDefinedTypeNameOrIdentifierPath>,
|
|
type_name: Option<TypeName>,
|
|
}
|
|
);
|
|
|
|
/// A wrapper around [IdentifierPath] for the [UsingForDirective].
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct FunctionIdentifierPath {
|
|
pub function: IdentifierPath,
|
|
}
|
|
|
|
ast_node!(
|
|
/// An import directive.
|
|
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,
|
|
}
|
|
);
|
|
|
|
/// A symbol alias.
|
|
///
|
|
/// Symbol aliases can be defined using the [ImportDirective].
|
|
#[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!(
|
|
/// A pragma directive.
|
|
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!("... {} fail: {e}", path_str);
|
|
panic!();
|
|
}
|
|
Ok(_) => {
|
|
println!("... {} ok", path_str);
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|