Skip to content

Commit 62a07a7

Browse files
committed
fix issue with indirect type loading in using * directives
1 parent d83403c commit 62a07a7

File tree

10 files changed

+180
-41
lines changed

10 files changed

+180
-41
lines changed

src/solidity_parser/ast/ast2builder.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -750,11 +750,16 @@ def scopes_for_type(self, node: solnodes1.AST1Node, ttype: solnodes2.Types, use_
750750
scope = ttype.value.x.scope
751751
assert isinstance(scope, symtab.FileScope)
752752
return [scope]
753-
754-
user_scopes = node.scope.find_user_type_scope(ttype.value.x.name.text)
755753

756-
filtered_scopes = []
754+
try:
755+
user_scopes = node.scope.find_user_type_scope(ttype.value.x.name.text)
756+
except symtab.TypeNotFound:
757+
# Weird situation where an object of type T is used in a contract during an intermediate computation but
758+
# T isn't imported. E.g. (x.y).z = f() where x.y is of type T and f returns an object of type S but
759+
# T isn't imported in the contract. This is the AddressSlot case in ERC1967Upgrade
760+
return [ttype.scope]
757761

762+
filtered_scopes = []
758763
for s in user_scopes:
759764
if s is not None and s.value != ttype.scope.value:
760765
if s.res_syms_single() != ttype.scope.res_syms_single():
@@ -763,23 +768,13 @@ def scopes_for_type(self, node: solnodes1.AST1Node, ttype: solnodes2.Types, use_
763768
continue
764769
filtered_scopes.append(s)
765770

766-
user_scopes = filtered_scopes
767-
768-
if not user_scopes:
769-
# Weird situation where an object of type T is used in a contract during an intermediate computation but
770-
# T isn't imported. E.g. (x.y).z = f() where x.y is of type T and f returns an object of type S but
771-
# T isn't imported in the contract. This is the AddressSlot case in ERC1967Upgrade
772-
scopes = [ttype.scope]
773-
else:
774-
scopes = user_scopes
775771
# "Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for
776772
# example this.balance. This is now forbidden and an explicit conversion to address must be done:
777773
# address(this).balance"
778774
# TODO: add versioncheck
779775
# if ttype.value.x.is_contract():
780776
# scopes.append(scope.find_type(solnodes1.AddressType(False)))
781-
782-
return scopes
777+
return filtered_scopes
783778
elif isinstance(ttype, soltypes.BuiltinType):
784779
scope = node.scope.find_single(ttype.name)
785780
elif isinstance(ttype, soltypes.MetaTypeType):

src/solidity_parser/ast/solnodes2.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ def __str__(self):
6363
def __repr__(self):
6464
return self.__str__()
6565

66+
def type_key(self, *args, **kwargs):
67+
return self.value.x.name.text
68+
69+
def code_str(self):
70+
return self.value.x.name.text
71+
6672
def is_builtin(self) -> bool:
6773
return False
6874

@@ -81,9 +87,6 @@ def can_implicitly_cast_from(self, actual_type: soltypes.Type) -> bool:
8187
def get_types_for_declared_type(self) -> list['TopLevelUnit']:
8288
return [self.value.x] + self.value.x.get_subtypes()
8389

84-
def code_str(self):
85-
return self.value.x.name.text
86-
8790

8891
@nodebase.NodeDataclass
8992
class SuperType(soltypes.Type):

src/solidity_parser/ast/symtab.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ def _add_to_results(possible_symbols: Collection, results: list, found_already:
118118
results.append(s)
119119

120120

121+
class TypeNotFound(Exception):
122+
pass
123+
121124
class Scopeable:
122125
"""Element that can be added as a child of a Scope"""
123126

@@ -393,6 +396,7 @@ def find_user_type_scope(self, name, find_base_symbol: bool = False, default=Non
393396
:param predicate: Optional function to filter during the search
394397
395398
:return: A single scope if find_base_symbol is True, or a list of scopes if find_base_symbol is False
399+
:raises TypeNotFound: if no type matching the name was found
396400
"""
397401

398402
if not default and not find_base_symbol:
@@ -463,6 +467,9 @@ def do_search(using_mode):
463467
if not result:
464468
result = default
465469

470+
if not result:
471+
raise TypeNotFound(f"Could not find scope for type: '{name}'")
472+
466473
return result.res_syms_single() if find_base_symbol else result
467474

468475
# These don't work because Pycharm is broken: See PY-42137
@@ -640,12 +647,17 @@ def create_builtin_scope(key, value=None, values=None, functions=None):
640647
return scope
641648

642649

643-
def type_key(ttype) -> str:
644-
return f'<type:{ttype.type_key()}>'
650+
def simple_name_resolver(scope, name):
651+
type_scope = scope.find_user_type_scope(name, find_base_symbol=True)
652+
return type_scope.aliases[0]
653+
645654

655+
def type_key(ttype, name_resolver=simple_name_resolver) -> str:
656+
return f'<type:{ttype.type_key(name_resolver)}>'
646657

647-
def meta_type_key(ttype) -> str:
648-
return f'<metatype:{ttype.type_key()}>'
658+
659+
def meta_type_key(ttype, name_resolver=simple_name_resolver) -> str:
660+
return f'<metatype:{ttype.type_key(name_resolver)}>'
649661

650662

651663
class RootScope(Scope):
@@ -720,7 +732,7 @@ def __init__(self, parser_version: version_util.Version):
720732
def address_object(payable):
721733
# key is <type: address> or <type: address payable>
722734
t = soltypes.AddressType(payable)
723-
scope = BuiltinObject(type_key(t), t)
735+
scope = BuiltinObject(type_key(t, None), t)
724736
scope.add(BuiltinValue('balance', uint()))
725737
scope.add(BuiltinValue('code', bytes()))
726738
scope.add(BuiltinValue('codehash', bytes32()))
@@ -1258,6 +1270,7 @@ def scope_name(self, base_name, node):
12581270
return f'<{base_name}>@{node.location}'
12591271

12601272
def find_using_target_scope_and_name(self, current_scope, target_type: soltypes.Type):
1273+
12611274
# TODO: Not sure if this is possible and I don't want to handle it(yet), just want to handle Types
12621275
# for target_type
12631276
if isinstance(target_type, solnodes.Ident):
@@ -1399,7 +1412,15 @@ def process_using_any_type(self, context: Context, node: solnodes.UsingDirective
13991412
if not func_def.parameters:
14001413
continue
14011414
target_type = func_def.parameters[0].var_type
1402-
target_type_scope, target_scope_name = self.find_using_target_scope_and_name(cur_scope, target_type)
1415+
try:
1416+
target_type_scope, target_scope_name = self.find_using_target_scope_and_name(cur_scope, target_type)
1417+
except TypeNotFound:
1418+
# when a contract has a 'using L for *' directive, it doesn't need to import any of the types that will
1419+
# be bound to by the first parameter of the library's functions => the target type lookup in the current
1420+
# scope might fail(if it wasnt imported). In this case, finding the base scope of the type by looking in
1421+
# the library scope(don't take any proxy scopes here as indirect using directives aren't
1422+
# inherited/imported).
1423+
target_type_scope, target_scope_name = self.find_using_target_scope_and_name(library_scope, target_type)
14031424

14041425
scope_to_add_to = self.get_proxy_scope_for_type(cur_scope, target_type, target_scope_name, target_type_scope, library_scope, check_lib=False)
14051426

src/solidity_parser/ast/types.py

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def is_void(self) -> bool:
8585
"""
8686
return False
8787

88-
def type_key(self):
88+
def type_key(self, *args, **kwargs):
8989
""" Returns a unique key for the type that can be used to cache types in the symbol table """
9090
return self.code_str()
9191

@@ -122,6 +122,9 @@ def can_implicitly_cast_from(self, actual_type: 'Type') -> bool:
122122
return True
123123
return actual_type.is_float()
124124

125+
def type_key(self, *args, **kwargs):
126+
raise ValueError('Float types do not have a type key')
127+
125128

126129
@NodeDataclass
127130
class VoidType(Type):
@@ -137,6 +140,9 @@ def code_str(self):
137140
def __str__(self):
138141
return '<void>'
139142

143+
def type_key(self, *args, **kwargs):
144+
raise ValueError('Void types do not have a type key')
145+
140146

141147
@NodeDataclass
142148
class ArrayType(Type):
@@ -146,6 +152,9 @@ class ArrayType(Type):
146152
def __str__(self): return f"{self.base_type}[]"
147153

148154
def code_str(self): return f'{self.base_type.code_str()}[]'
155+
156+
def type_key(self, name_resolver=None, *args, **kwargs):
157+
return f"{self.base_type.type_key(name_resolver, *args, **kwargs)}[]"
149158

150159
def is_builtin(self) -> bool:
151160
# e.g. byte[] is builtin, string[] is builtin, MyContract[] is not
@@ -183,6 +192,12 @@ class FixedLengthArrayType(ArrayType):
183192

184193
def __str__(self): return f"{self.base_type}[{self.size}]"
185194

195+
def code_str(self):
196+
return f'{self.base_type.code_str()}[{str(self.size)}]'
197+
198+
def type_key(self, name_resolver=None, *args, **kwargs):
199+
return f"{self.base_type.type_key(name_resolver, *args, **kwargs)}[{str(self.size)}]"
200+
186201
def is_fixed_size(self) -> bool:
187202
return True
188203

@@ -200,9 +215,6 @@ def can_implicitly_cast_from(self, actual_type: 'Type') -> bool:
200215

201216
return False
202217

203-
def code_str(self):
204-
return f'{self.base_type.code_str()}[{str(self.size)}]'
205-
206218

207219
@NodeDataclass
208220
class VariableLengthArrayType(ArrayType):
@@ -214,6 +226,10 @@ def __str__(self): return f"{self.base_type}[{self.size}]"
214226
def code_str(self):
215227
return f'{self.base_type.code_str()}[{self.size.code_str()}]'
216228

229+
def type_key(self, name_resolver=None, *args, **kwargs):
230+
# the size bit is a bit tricky as it might not be a literal, just stringify it for now
231+
return f"{self.base_type.type_key(name_resolver, *args, **kwargs)}[{self.size.code_str()}]"
232+
217233

218234
@NodeDataclass
219235
class AddressType(Type):
@@ -222,6 +238,9 @@ class AddressType(Type):
222238

223239
def __str__(self): return f"address{' payable' if self.is_payable else ''}"
224240

241+
def code_str(self):
242+
return 'address' + (' payable' if self.is_payable else '')
243+
225244
def can_implicitly_cast_from(self, actual_type: Type) -> bool:
226245
# address_payable(actual_type) can be cast to address implicitly
227246
if actual_type.is_address():
@@ -257,9 +276,6 @@ def is_builtin(self) -> bool:
257276
def is_address(self) -> bool:
258277
return True
259278

260-
def code_str(self):
261-
return 'address' + (' payable' if self.is_payable else '')
262-
263279

264280
@NodeDataclass
265281
class ByteType(Type):
@@ -309,11 +325,14 @@ def can_implicitly_cast_from(self, actual_type: 'Type') -> bool:
309325
return super().can_implicitly_cast_from(actual_type)
310326

311327
def __str__(self):
312-
return self.code_str()
328+
return 'bytes'
313329

314330
def code_str(self):
315331
return 'bytes'
316332

333+
def type_key(self, *args, **kwargs):
334+
return 'bytes'
335+
317336

318337
@NodeDataclass
319338
class IntType(Type):
@@ -384,15 +403,18 @@ class StringType(ArrayType):
384403

385404
def __str__(self): return "string"
386405

406+
def code_str(self):
407+
return 'string'
408+
409+
def type_key(self, *args, **kwargs):
410+
return 'string'
411+
387412
def is_builtin(self) -> bool:
388413
return True
389414

390415
def is_string(self) -> bool:
391416
return True
392417

393-
def code_str(self):
394-
return 'string'
395-
396418

397419
@NodeDataclass
398420
class PreciseStringType(StringType):
@@ -432,6 +454,12 @@ def _name(ident):
432454
return (' ' + str(ident)) if ident else ''
433455
return f"({self.src}{_name(self.src_name)} => {self.dst}{_name(self.dst_name)})"
434456

457+
def code_str(self):
458+
return str(self)
459+
460+
def type_key(self, name_resolver=None, *args, **kwargs):
461+
return f"({self.src.type_key(name_resolver, *args, **kwargs)} => {self.dst.type_key(name_resolver, *args, **kwargs)})"
462+
435463
def is_mapping(self) -> bool:
436464
return True
437465

@@ -450,9 +478,6 @@ def flatten(self) -> list[Type]:
450478
next_link = None
451479
return result
452480

453-
def code_str(self):
454-
return str(self)
455-
456481

457482
@NodeDataclass
458483
class UserType(Type):
@@ -465,6 +490,12 @@ class UserType(Type):
465490

466491
def __str__(self): return str(self.name)
467492

493+
def type_key(self, name_resolver=None, *args, **kwargs):
494+
if name_resolver is None:
495+
raise ValueError(f'Cannot resolve {self.name} without a name resolver')
496+
else:
497+
return name_resolver(self.scope, self.name.text)
498+
468499

469500
@NodeDataclass
470501
class BuiltinType(Type):
@@ -530,10 +561,10 @@ def code_str(self):
530561
def __str__(self):
531562
return self.code_str()
532563

533-
def type_key(self):
564+
def type_key(self, name_resolver=None, *args, **kwargs):
534565
# doesn't include modifiers for now
535-
input_params = ', '.join([p.type_key() for p in self.inputs])
536-
output_params = ', '.join([p.type_key() for p in self.outputs])
566+
input_params = ', '.join([p.type_key(name_resolver, *args, **kwargs) for p in self.inputs])
567+
output_params = ', '.join([p.type_key(name_resolver, *args, **kwargs) for p in self.outputs])
537568
return f'function ({input_params}) returns ({output_params})'
538569

539570

@@ -557,6 +588,9 @@ def code_str(self):
557588
def __str__(self):
558589
return f'({", ".join(str(t) for t in self.ttypes)})'
559590

591+
def type_key(self, name_resolver=None, *args, **kwargs):
592+
return f'({", ".join(t.type_key(name_resolver, *args, **kwargs) for t in self.ttypes)})'
593+
560594

561595
@NodeDataclass
562596
class MetaTypeType(Type):
@@ -576,6 +610,8 @@ def code_str(self):
576610
def __str__(self):
577611
return f'type({self.ttype})'
578612

613+
# TODO: metatype typekey
614+
579615

580616
@NodeDataclass
581617
class VarType(Type):
@@ -590,6 +626,9 @@ class VarType(Type):
590626

591627
def __str__(self): return "var"
592628

629+
def type_key(self, name_resolver=None, *args, **kwargs):
630+
raise ValueError('Var types do not have a type key')
631+
593632

594633
@NodeDataclass
595634
class AnyType(Type):
@@ -601,3 +640,5 @@ class AnyType(Type):
601640

602641
def __str__(self): return "*"
603642

643+
def type_key(self, name_resolver=None, *args, **kwargs):
644+
raise ValueError('Any types do not have a type key')

test/solidity_parser/ast/snapshots/snap_test_parsing.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88
snapshots = Snapshot()
99

10+
snapshots['TestASTJSONCases::test_debug 1'] = [
11+
GenericRepr("FileDefinition(source_unit_name='using_for_directive.sol', name=Ident(text='using_for_directive.sol'), parts=[FunctionDefinition(name=Ident(text='f'), inputs=[Parameter(var=Var(name=Ident(text=None), ttype=IntType(is_signed=False, size=256), location=None))], outputs=[], modifiers=[], code=Block(stmts=[], is_unchecked=False), markers=[])])"),
12+
GenericRepr("LibraryDefinition(source_unit_name='using_for_directive.sol', name=Ident(text='L'), parts=[], type_overrides=[])"),
13+
GenericRepr("ContractDefinition(source_unit_name='using_for_directive.sol', name=Ident(text='C'), is_abstract=False, inherits=[], parts=[], type_overrides=[])")
14+
]
15+
1016
snapshots['TestASTJSONCases::test_success_path_abstract_contract_sol 1'] = [
1117
GenericRepr("ContractDefinition(source_unit_name='abstract_contract.sol', name=Ident(text='C'), is_abstract=True, inherits=[], parts=[FunctionDefinition(name=Ident(text='constructor'), inputs=[], outputs=[], modifiers=[], code=Block(stmts=[], is_unchecked=False), markers=[<FunctionMarker.CONSTRUCTOR: 1>])], type_overrides=[])")
1218
]

test/solidity_parser/ast/test_parsing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,13 @@ def test_ast_internal_function_different_ids_export(self):
118118
self._load_separated_file('ast_internal_function_different_ids_export.sol')
119119

120120
# def test_debug(self):
121-
# self._load('using_for_directive.sol')
121+
# self._load('non_utf8.sol')
122122
# units = self.ast2_builder.get_top_level_units()
123123
# self.assertMatchSnapshot(units)
124124
#
125125
# print("x")
126126

127+
127128
class TestSemanticTestCases(LibSolidityTestBase, SnapshotTestCase):
128129
SRC_DIR = 'testcases/libsolidity/semanticTests'
129130
def __init__(self, *args, **kwargs):

0 commit comments

Comments
 (0)