From ce38dc7694ca1ada5bdaeb39579cb8729271a9e4 Mon Sep 17 00:00:00 2001 From: Bilal Date: Thu, 4 Dec 2025 12:36:13 +0900 Subject: [PATCH 1/3] add prevrandao alias --- src/solidity_parser/ast/ast2builder.py | 3 +++ src/solidity_parser/ast/symtab.py | 4 ++-- .../ast/snapshots/snap_test_ast2builder.py | 8 +++++++- testcases/type_tests/basic_types.sol | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/solidity_parser/ast/ast2builder.py b/src/solidity_parser/ast/ast2builder.py index b38eb5b..5338408 100644 --- a/src/solidity_parser/ast/ast2builder.py +++ b/src/solidity_parser/ast/ast2builder.py @@ -2002,6 +2002,9 @@ def z(): if isinstance(sym, symtab.BuiltinValue): if isinstance(base_type, soltypes.BuiltinType): # e.g. msg.gas, where the base is a builtin object + # TODO: should we use the given mname here or should we use the canonical name tagged to the + # BuiltinValue? In most cases it's the same but not all, e.g. aliased symbols: block.prevrandao and + # block.difficulty return solnodes2.GlobalValue(f'{base_type.name}.{mname}', self.type_helper.map_type(sym.ttype)) else: # e.g. myarray.length, 'length' is builtin to the array type(i.e. not a concrete field) diff --git a/src/solidity_parser/ast/symtab.py b/src/solidity_parser/ast/symtab.py index f6b335c..f58b8da 100644 --- a/src/solidity_parser/ast/symtab.py +++ b/src/solidity_parser/ast/symtab.py @@ -628,7 +628,7 @@ def __init__(self, name: str, input_types: list[soltypes.Type] | None, output_ty class BuiltinValue(Symbol): - def __init__(self, name: str, ttype: soltypes.Type): + def __init__(self, name: Aliases, ttype: soltypes.Type): ScopeAndSymbol.__init__(self, name, None) self.ttype = ttype @@ -707,7 +707,7 @@ def __init__(self, parser_version: version_util.Version): block_object.add(BuiltinValue('basefee', uint())) block_object.add(BuiltinValue('chainid', uint())) block_object.add(BuiltinValue('coinbase', soltypes.AddressType(True))) - block_object.add(BuiltinValue('difficulty', uint())) + block_object.add(BuiltinValue(['difficulty', 'prevrandao'], uint())) block_object.add(BuiltinValue('gaslimit', uint())) block_object.add(BuiltinValue('number', uint())) # Add blobbasefee only for Solidity 0.8.24 and above diff --git a/test/solidity_parser/ast/snapshots/snap_test_ast2builder.py b/test/solidity_parser/ast/snapshots/snap_test_ast2builder.py index 005d33c..2cc0de4 100644 --- a/test/solidity_parser/ast/snapshots/snap_test_ast2builder.py +++ b/test/solidity_parser/ast/snapshots/snap_test_ast2builder.py @@ -165,7 +165,13 @@ "CallFunction(callee=New(type_name=UserType(name=Ident(text='AllTypesExample'))), special_call_options=[], args=[]) :: ResolvedUserType(AllTypesExample)", "New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)", "CallFunction(callee=New(type_name=UserType(name=Ident(text='AllTypesExample'))), special_call_options=[], args=[]) :: ResolvedUserType(AllTypesExample)", - "New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)" + "New(type_name=UserType(name=Ident(text='AllTypesExample'))) :: ResolvedUserType(AllTypesExample)", + "GetMember(obj_base=Ident(text='block'), name=Ident(text='prevrandao')) :: [IntType(is_signed=False, size=256)]", + "GetMember(obj_base=Ident(text='block'), name=Ident(text='difficulty')) :: [IntType(is_signed=False, size=256)]", + "BinaryOp(left=Ident(text='x'), right=Ident(text='y'), op=) :: uint256", + "GetMember(obj_base=Ident(text='block'), name=Ident(text='prevrandao')) :: [IntType(is_signed=False, size=256)]", + "GetMember(obj_base=Ident(text='block'), name=Ident(text='difficulty')) :: [IntType(is_signed=False, size=256)]", + "BinaryOp(left=Ident(text='x'), right=Ident(text='y'), op=) :: uint256" ] snapshots['TestSolidityTypeHelper::test_get_function_expr_type 1'] = [ diff --git a/testcases/type_tests/basic_types.sol b/testcases/type_tests/basic_types.sol index ac28b21..aaf79b8 100644 --- a/testcases/type_tests/basic_types.sol +++ b/testcases/type_tests/basic_types.sol @@ -137,4 +137,10 @@ contract AllTypesExample { function new_deploy_contract() { AllTypesExample e = new AllTypesExample(); } + + function blockDifficultyAndPrevRandaoAlias() public returns (uint) { + uint x = block.prevrandao; + uint y = block.difficulty; + return x + y; + } } \ No newline at end of file From e2d9c7d9975778c0183c15da2801c2ed1b147fb5 Mon Sep 17 00:00:00 2001 From: Bilal Date: Thu, 4 Dec 2025 14:07:20 +0900 Subject: [PATCH 2/3] fix missing value attr on ast2 var decls --- src/solidity_parser/ast/ast2builder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/solidity_parser/ast/ast2builder.py b/src/solidity_parser/ast/ast2builder.py index 5338408..7946bf5 100644 --- a/src/solidity_parser/ast/ast2builder.py +++ b/src/solidity_parser/ast/ast2builder.py @@ -2496,7 +2496,8 @@ def refine_node(n): if hasattr(ast1_node, 'modifiers'): ast2_node.modifiers = [self.modifier(x) for x in ast1_node.modifiers] if ast1_node.initial_value: - ast2_node.initial_value = self.refine_expr(ast1_node.initial_value, is_assign_rhs=True) + self.error_handler.assert_error("expected type with 'value' field", isinstance(ast2_node, (solnodes2.ConstantVariableDeclaration, solnodes2.StateVariableDeclaration))) + ast2_node.value = self.refine_expr(ast1_node.initial_value, is_assign_rhs=True) return refine_node(ast2_node) if isinstance(ast1_node, solnodes1.EventDefinition): From 6a8faba979e89fa82c204e700badd01635bde231 Mon Sep 17 00:00:00 2001 From: Bilal Date: Tue, 9 Dec 2025 11:04:47 +0900 Subject: [PATCH 3/3] tmp: misc type fixes --- src/solidity_parser/ast/ast2builder.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/solidity_parser/ast/ast2builder.py b/src/solidity_parser/ast/ast2builder.py index 7946bf5..3ce8481 100644 --- a/src/solidity_parser/ast/ast2builder.py +++ b/src/solidity_parser/ast/ast2builder.py @@ -36,7 +36,7 @@ def __init__(self, create_state, quiet_errors=True): # whether to throw a caught error immediately or store it in the caught_errors list for a client to consume # later, this is used for testing where fast errors can be caught and fail the test quickly - self.quiet_errors = quiet_errors + self.quiet_errors = False self.caught_errors = [] # current state of the builder self.state = None @@ -201,6 +201,7 @@ def get_expr_type(self, expr: solnodes1.Expr | soltypes.Type, allow_multiple=Fal :return: The AST2 type of the expression or a list of types if allow_multiple is True """ + if isinstance(expr, soltypes.Type): # already have a type, either return it or resolve it to make sure it's AST2 and not AST1 only return self.map_type(expr) @@ -893,17 +894,29 @@ def map_type(self, ttype: soltypes.Type) -> solnodes2.Types: return soltypes.StringType() elif isinstance(ttype, soltypes.VariableLengthArrayType): base_type = self.map_type(ttype.base_type) - size_type = self.get_expr_type(ttype.size) + if isinstance(ttype.size, solnodes2.Expr): + size_type = ttype.size.type_of() + elif isinstance(ttype.size, solnodes2.Types): + size_type = ttype.size + else: + size_type = self.get_expr_type(ttype.size) + # Fix for some weird grammar parsing issues where a fixed length array type is parsed as a variable length # array type with a literal passed as the size expr, so here we change it to a fixed one # Check for isinstance(ttype.size, solnodes1.Literal) as a compile time constant expr, e.g. Record[2**253] # is a fixed sized but FixedLengthArrayType needs an int not an expr that computes an int. - if size_type.is_int() and size_type.is_literal_type() and isinstance(ttype.size, solnodes1.Literal): + is_const_int = size_type.is_int() and size_type.is_literal_type() and isinstance(ttype.size, solnodes1.Literal) + + if is_const_int: size = ttype.size.value assert isinstance(size, int) return soltypes.FixedLengthArrayType(base_type, size) else: - return soltypes.VariableLengthArrayType(base_type, self.builder.refine_expr(ttype.size)) + if isinstance(ttype.size, solnodes2.Expr): + size = deepcopy(ttype.size) + else: + size = self.builder.refine_expr(ttype.size) + return soltypes.VariableLengthArrayType(base_type, size) elif isinstance(ttype, soltypes.FixedLengthArrayType): return soltypes.FixedLengthArrayType(self.map_type(ttype.base_type), ttype.size) elif isinstance(ttype, soltypes.ArrayType): @@ -2402,7 +2415,10 @@ def _make_new_node(n): ast2_node.values = [solnodes2.EnumMember(solnodes2.Ident(n.text)) for n in ast1_node.values] if isinstance(ast1_node, (solnodes1.StateVariableDeclaration, solnodes1.ConstantVariableDeclaration)): - ast2_node.ttype = self.type_helper.map_type(ast1_node.var_type) + ast2_node.ttype = ( + self.type_helper.get_expr_type(ast1_node.initial_value) if ast1_node.initial_value + else self.type_helper.map_type(ast1_node.var_type) + ) if isinstance(ast1_node, solnodes1.EventDefinition): ast2_node.inputs = [solnodes2.EventParameter(solnodes2.Ident(p.var_name.text if p.var_name else f'