Skip to content

Commit 102cf46

Browse files
authored
Merge pull request #35 from Zellic/sol826
Solidity 0.8.26 Support + Misc fixes
2 parents 6b08545 + 6eaecce commit 102cf46

15 files changed

+373
-179
lines changed

CHANGELOG.MD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
### [0.1.23] - 2025-05-19
9+
- Added `FunctionParameter` for input parameters of `FunctionType`s (required for named argument support)
10+
- Changed `FunctionType` field `inputs` to `input_params`
11+
- Added `block.blobbasefee` builtin variable
12+
- Added `blobhash` builtin function
13+
- Added new `require` function that takes an `ErrorType`
14+
- Added `Type.is_user_error`
15+
- Added `ErrorType` to make allow class error objects
16+
- Added `UserDefinedErrorType` for solidity `error` 'function' types
17+
- Added `CreateError` AST2 node for first class error initialisation
18+
- Changed AST2 `RevertWithError` to take a `CreateError` object instead of a type and list of args
19+
- Changed AST1 `EventParameter` and `ErrorParameter` to use `var_name` instead of `name` to be consistent with Parameter
20+
- Changed AST1 `Parameter` to use `soltypes.Type` for `var_type` instead of `Ident`
21+
- Fixed hashing issue with `NodeList`
22+
- Fixed some issues with function type widening with
23+
-
824
### [0.1.22] - 2024-08-29
925
- Fixed issues with external function call options(gas/value) being confused with name function arguments
1026
- Renamed `solnodes.CallFunction.modifiers` to `special_call_options`

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def gen(self):
2626

2727
setup(
2828
name='solidity-parser',
29-
version='0.1.22',
29+
version='0.1.23',
3030

3131
install_requires=[
3232
"antlr4-python3-runtime==4.11.1",

src/solidity_parser/ast/ast2builder.py

Lines changed: 138 additions & 47 deletions
Large diffs are not rendered by default.

src/solidity_parser/ast/nodebase.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ def __init__(self, parent: T, seq=()):
127127
super().__init__(seq)
128128
self.parent = parent
129129

130+
# shims so the NodeDataclass decorator can generate a hash for this list
131+
def get_children(self) -> Generator['Node', None, None]:
132+
yield from list(self)
133+
134+
def get_all_children(self) -> Generator['Node', None, None]:
135+
for direct_child in self.get_children():
136+
yield direct_child
137+
yield from direct_child.get_all_children()
138+
130139
def __str__(self):
131140
return list.__str__(self)
132141

@@ -143,12 +152,12 @@ def __delitem__(self, key):
143152
self._set_dirty(key, None)
144153

145154
def __setslice__(self, i, j, sequence):
146-
raise NotImplemented
155+
raise NotImplementedError()
147156

148157
def __eq__(self, other):
149158
if isinstance(other, list):
150159
return super().__eq__(other)
151-
raise NotImplemented
160+
raise NotImplementedError(f"Can't compare NodeList to {other}")
152161

153162
def append(self, __object):
154163
ret = super().append(__object)

src/solidity_parser/ast/parsers/parsers060.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -264,24 +264,15 @@ def _mapping_type(parser, mapping_type: SolidityParser.MappingContext):
264264
)
265265

266266

267-
def params_to_types(params: solnodes.Parameter):
268-
# TODO: add name + storage to FunctionType. Is it even necessary?
269-
# old solnodes1.FunctionType had this but solnodes2.FunctionType didn't and now types.FunctionType doesn't either
270-
# so this informtion isn't stored.
271-
# Idea: create a new mixin that can be added to all the Type subclasses OR create a new NamedType that has the
272-
# actual type as an attr and redirects Type calls to the attr. e.g.
273-
# class NamedType(Type):
274-
# name: Ident
275-
# ttype: Type
276-
# storage: ...
277-
# def __getattr__(self, name):
278-
# # checks...
279-
# return getattr(self.ttype, name)
267+
def params_to_types(params: list[solnodes.Parameter]):
280268
return [p.var_type for p in params]
281269

270+
def input_params_to_types(params: list[solnodes.Parameter]):
271+
return [soltypes.FunctionParameter(p.var_name, p.var_type) for p in params]
272+
282273
def _function_type_name(parser, function_type: SolidityParser.FunctionTypeNameContext):
283274
return soltypes.FunctionType(
284-
params_to_types(parser.make(function_type.parameterList(), default=[])),
275+
input_params_to_types(parser.make(function_type.parameterList(), default=[])),
285276
params_to_types(parser.make(function_type.returnParameters(), default=[])),
286277
parser.make(function_type.modifierList(), default=[])
287278
)

src/solidity_parser/ast/parsers/parsers080.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ def _type_name(parser, type_name: SolidityParser.TypeNameContext):
552552

553553
def _function_type_name(parser, function_type: SolidityParser.FunctionTypeNameContext):
554554
return soltypes.FunctionType(
555-
parsers060.params_to_types(parser.make(function_type.arguments, default=[])),
555+
parsers060.input_params_to_types(parser.make(function_type.arguments, default=[])),
556556
parsers060.params_to_types(parser.make(function_type.returnParameters, default=[])),
557557
parser.make_all_rules(function_type.visibility()) + parser.make_all_rules(function_type.stateMutability())
558558
)

src/solidity_parser/ast/solnodes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ class VarDecl(Stmt):
264264

265265
@dataclass
266266
class Parameter(AST1Node):
267-
var_type: Ident
267+
var_type: soltypes.Type
268268
var_loc: Location
269269
var_name: Ident
270270

@@ -511,7 +511,7 @@ class UserValueType(SourceUnit, ContractPart):
511511
@dataclass
512512
class EventParameter(AST1Node):
513513
var_type: soltypes.Type
514-
name: Ident
514+
var_name: Ident
515515
is_indexed: bool
516516

517517

@@ -525,7 +525,7 @@ class EventDefinition(ContractPart):
525525
@dataclass
526526
class ErrorParameter(AST1Node):
527527
var_type: soltypes.Type
528-
name: Ident
528+
var_name: Ident
529529

530530

531531
@dataclass
@@ -601,5 +601,6 @@ def has_modifier_kind(node, *kinds: VisibilityModifierKind | MutabilityModifierK
601601
Types: typing.TypeAlias = (soltypes.VariableLengthArrayType | soltypes.VoidType | soltypes.IntType
602602
| soltypes.FunctionType | soltypes.ArrayType | soltypes.BytesType | soltypes.BoolType
603603
| soltypes.AnyType | soltypes.MappingType | soltypes.UserType | soltypes.StringType
604-
| soltypes.FixedLengthArrayType | soltypes.AddressType | soltypes.ByteType)
604+
| soltypes.FixedLengthArrayType | soltypes.AddressType | soltypes.ByteType
605+
| soltypes.ErrorType )
605606

src/solidity_parser/ast/solnodes2.py

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,35 @@ def type_of(self) -> soltypes.Type:
4848
class Modifier(AST2Node, ABC):
4949
pass
5050

51+
@nodebase.NodeDataclass
52+
class UserDefinedErrorType(soltypes.Type):
53+
# Use a ref because this type doesn't "own" the ErrorDefinition node instance
54+
value: nodebase.Ref['ErrorDefinition'] = field(repr=False)
55+
56+
def is_user_error(self) -> bool:
57+
return True
58+
59+
def is_user_type(self) -> bool:
60+
return True
61+
62+
def __str__(self):
63+
return f'error({self.value.x.name.text})'
64+
65+
def __repr__(self):
66+
return self.__str__()
67+
68+
def code_str(self):
69+
return self.value.x.name.text
70+
71+
def type_key(self, *args, **kwargs):
72+
return self.value.x.name.text
73+
74+
def can_implicitly_cast_from(self, actual_type: 'Type') -> bool:
75+
if super().can_implicitly_cast_from(actual_type):
76+
return True
77+
if actual_type.is_user_error():
78+
return actual_type.value.x == self.value.x
79+
return False
5180

5281
@nodebase.NodeDataclass
5382
class ResolvedUserType(soltypes.Type):
@@ -1058,6 +1087,18 @@ def code_str(self):
10581087
return f'{self.ttype.code_str()}({", ".join(e.code_str() for e in self.args)})'
10591088

10601089

1090+
@nodebase.NodeDataclass
1091+
class CreateError(Expr):
1092+
ttype: UserDefinedErrorType
1093+
args: list[Expr]
1094+
1095+
def type_of(self) -> soltypes.Type:
1096+
return self.ttype
1097+
1098+
def code_str(self):
1099+
return f'{self.ttype.code_str()}({", ".join(e.code_str() for e in self.args)})'
1100+
1101+
10611102
@nodebase.NodeDataclass
10621103
class CreateAndDeployContract(Expr):
10631104
ttype: ResolvedUserType
@@ -1072,16 +1113,30 @@ def code_str(self):
10721113

10731114

10741115
def check_arg_types(args: list[Expr], f: FunctionDefinition) -> bool:
1075-
named_args = {a.name.text: a.expr.type_of() for a in args if isinstance(a, NamedArgument)}
1116+
# weird edge case: imagine a call x.abc({a:1,b:2}) where we have a "using statement" for x. The statement gets
1117+
# converted to a DirectCall to X.abc, with the args (x, {a:1}, {b:2}), i.e.a mix of 1 positional argument the rest
1118+
# named
1119+
possible_direct_call = len(args) > 0 and not isinstance(args[0], NamedArgument) and all([isinstance(a, NamedArgument) for a in args[1:]])
1120+
1121+
named_args = {
1122+
a.name.text: a.expr.type_of()
1123+
for a in (args[1:] if possible_direct_call else args) if isinstance(a, NamedArgument)
1124+
}
10761125

10771126
if len(named_args) > 0:
1078-
func_params = {p.var.name.text: p.var.ttype for p in f.inputs}
1127+
func_params = {p.var.name.text: p.var.ttype for p in (f.inputs[1:] if possible_direct_call else f.inputs)}
10791128

10801129
if set(func_params.keys()) != set(named_args.keys()):
10811130
return False
10821131

10831132
f_types, c_types = [], []
10841133

1134+
if possible_direct_call:
1135+
# want to match first arg by direct
1136+
f_types.append(f.inputs[0].var.ttype)
1137+
c_types.append(args[0].type_of())
1138+
1139+
# f_types[i] and c_types[i] are expected and provided types of an arg 'x'
10851140
for k, v in named_args.items():
10861141
f_types.append(func_params[k])
10871142
c_types.append(v)
@@ -1117,6 +1172,8 @@ def resolve_call(self) -> FunctionDefinition:
11171172
unit = self.ttype.value.x
11181173
matching_name_funcs = [p for p in unit.parts if isinstance(p, FunctionDefinition) and p.name.text == self.name.text]
11191174
matching_param_types = [f for f in matching_name_funcs if self.check_arg_types(f)]
1175+
if len(matching_name_funcs) > 0:
1176+
self.check_arg_types(matching_name_funcs[0])
11201177
assert len(matching_param_types) == 1
11211178
return matching_param_types[0]
11221179

@@ -1259,9 +1316,11 @@ class GetFunctionPointer(Expr):
12591316
def type_of(self) -> soltypes.Type:
12601317
def ts(params):
12611318
return [p.var.ttype for p in params]
1319+
def its(params: list[ErrorParameter | Parameter | EventParameter]):
1320+
return [soltypes.FunctionParameter(p.var.name, p.var.ttype) for p in params]
12621321
f = self.func.x
12631322
modifiers = f.modifiers if hasattr(f, 'modifiers') else []
1264-
return soltypes.FunctionType(ts(f.inputs), ts(f.outputs), modifiers)
1323+
return soltypes.FunctionType(its(f.inputs), ts(f.outputs), modifiers)
12651324

12661325
def code_str(self):
12671326
return f'fptr({self.func.x.parent.descriptor()}::{self.func.x.name.text})'
@@ -1283,11 +1342,10 @@ class Revert(Stmt):
12831342

12841343
@nodebase.NodeDataclass
12851344
class RevertWithError(Revert):
1286-
error: nodebase.Ref[ErrorDefinition]
1287-
args: list[Expr]
1345+
error: CreateError
12881346

12891347
def code_str(self):
1290-
return f'revert {self.error.x.name.text}({", ".join(e.code_str() for e in self.args)});'
1348+
return f'revert {self.error.code_str()};'
12911349

12921350

12931351
@nodebase.NodeDataclass
@@ -1352,4 +1410,5 @@ class UnprocessedCode(Stmt):
13521410
| soltypes.AnyType | soltypes.MappingType | soltypes.StringType | soltypes.AddressType
13531411
| soltypes.FixedLengthArrayType | soltypes.ByteType | soltypes.MetaTypeType
13541412
| soltypes.TupleType | soltypes.PreciseIntType | soltypes.PreciseIntType
1355-
| soltypes.BuiltinType | ResolvedUserType | SuperType | soltypes.FloatType)
1413+
| soltypes.BuiltinType | ResolvedUserType | SuperType | soltypes.FloatType
1414+
| soltypes.ErrorType | UserDefinedErrorType)

src/solidity_parser/ast/symtab.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,9 @@ def __init__(self, parser_version: version_util.Version):
710710
block_object.add(BuiltinValue('difficulty', uint()))
711711
block_object.add(BuiltinValue('gaslimit', uint()))
712712
block_object.add(BuiltinValue('number', uint()))
713+
# Add blobbasefee only for Solidity 0.8.24 and above
714+
if parser_version and parser_version.is_enforced_in(version_util.Version(0, 8, 24)):
715+
block_object.add(BuiltinValue('blobbasefee', uint()))
713716
block_object.add(now_symbol := BuiltinValue('timestamp', uint())) # now was used pre solidity 0.7 for block.timestamp
714717
self.add(block_object)
715718

@@ -768,11 +771,16 @@ def address_object(payable):
768771

769772
self.add(BuiltinFunction('gasleft', [], [uint()]))
770773

771-
self.add(BuiltinFunction('blobhash', [uint()], [bytes32()]))
774+
# Add blobhash only for Solidity 0.8.24 and above
775+
if parser_version and parser_version.is_enforced_in(version_util.Version(0, 8, 24)):
776+
self.add(BuiltinFunction('blobhash', [uint()], [bytes32()]))
772777
self.add(BuiltinFunction('blockhash', [uint()], [bytes32()]))
773778

774779
self.add(BuiltinFunction('require', [soltypes.BoolType(), soltypes.StringType()], []))
775780
self.add(BuiltinFunction('require', [soltypes.BoolType()], []))
781+
# Add require(bool, error) overload for Solidity 0.8.26 and above
782+
if parser_version and parser_version.is_enforced_in(version_util.Version(0, 8, 26)):
783+
self.add(BuiltinFunction('require', [soltypes.BoolType(), soltypes.ErrorType()], []))
776784

777785
self.add(BuiltinFunction('assert', [soltypes.BoolType()], []))
778786

@@ -1642,10 +1650,10 @@ def make_symbols_for_node(self, node, context: Context, build_skeletons: bool, v
16421650
else:
16431651
return self.make_symbol(node, name=node.var_name.text)
16441652
elif isinstance(node, solnodes.EventParameter):
1645-
if node.name is None:
1653+
if node.var_name is None:
16461654
name = f'<unnamed_paramter:{visit_index}'
16471655
else:
1648-
name = node.name.text
1656+
name = node.var_name.text
16491657
return self.make_symbol(node, name=name)
16501658
elif isinstance(node, solnodes.VarDecl):
16511659
return None # TODO: this is a sentinel

0 commit comments

Comments
 (0)