Skip to content

Commit

Permalink
fix not-found functions, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper committed Apr 13, 2024
1 parent adbf6b8 commit 6650768
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 26 deletions.
32 changes: 9 additions & 23 deletions tests/functional/codegen/modules/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from vyper.compiler import compile_code
from vyper.utils import method_id
from vyper.exceptions import StructureException


def test_simple_export(make_input_bundle, get_contract):
Expand Down Expand Up @@ -204,6 +203,7 @@ def _send_transaction(c, method_sig):
data = method_id(method_sig)
with tx_failed():
w3.eth.send_transaction({"to": c.address, "data": data})

return _send_transaction


Expand Down Expand Up @@ -247,7 +247,7 @@ def test_exported_fun_part_of_interface(get_contract, make_input_bundle):
lib1 = """
@external
def bar() -> uint256:
return 1
return 1
"""
lib2 = """
import lib1
Expand All @@ -264,7 +264,9 @@ def foo() -> uint256:
assert c.foo() == 2


def test_imported_module_not_part_of_interface(send_failing_tx_to_signature, get_contract, make_input_bundle):
def test_imported_module_not_part_of_interface(
send_failing_tx_to_signature, get_contract, make_input_bundle
):
main = """
import lib2
Expand All @@ -273,7 +275,7 @@ def test_imported_module_not_part_of_interface(send_failing_tx_to_signature, get
lib1 = """
@external
def bar() -> uint256:
return 1
return 1
"""
lib2 = """
import lib1
Expand All @@ -288,24 +290,9 @@ def foo() -> uint256:
send_failing_tx_to_signature(c, "bar()")


def test_interface_export_collision(send_failing_tx_to_signature, get_contract, make_input_bundle):
main = """
import lib1
exports: lib1.__interface__
exports: lib1.bar
"""
lib1 = """
@external
def bar() -> uint256:
return 1
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
with pytest.raises(StructureException):
get_contract(main, input_bundle=input_bundle)


def test_export_unimplemented_interface(send_failing_tx_to_signature, get_contract, make_input_bundle):
def test_export_unimplemented_interface(
send_failing_tx_to_signature, get_contract, make_input_bundle
):
ifoo = """
@external
def foo() -> uint256:
Expand All @@ -331,4 +318,3 @@ def bar() -> uint256:
c = get_contract(main, input_bundle=input_bundle)
assert c.foo() == 1
send_failing_tx_to_signature(c, "bar()")

72 changes: 72 additions & 0 deletions tests/functional/syntax/modules/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,75 @@ def bar():
assert e.value.prev_decl.col_offset == 9
assert e.value.prev_decl.node_source_code == "lib1.foo"
assert e.value.prev_decl.module_node.path == "main.vy"


def test_interface_export_collision(make_input_bundle):
main = """
import lib1
exports: lib1.__interface__
exports: lib1.bar
"""
lib1 = """
@external
def bar() -> uint256:
return 1
"""
input_bundle = make_input_bundle({"lib1.vy": lib1})
with pytest.raises(StructureException) as e:
compile_code(main, input_bundle=input_bundle)
assert e.value._message == "already exported!"


def test_export_missing_function(make_input_bundle):
ifoo = """
@external
def do_xyz():
...
"""
lib1 = """
import ifoo
@external
@view
def bar() -> uint256:
return 1
"""
main = """
import lib1
exports: lib1.ifoo
"""
input_bundle = make_input_bundle({"lib1.vy": lib1, "ifoo.vyi": ifoo})
with pytest.raises(StructureException) as e:
compile_code(main, input_bundle=input_bundle)
assert e.value._message == "requested `lib1.ifoo` but `lib1.do_xyz` is not implemented"


def test_export_selector_conflict(make_input_bundle):
ifoo = """
@external
def gsf():
...
"""
lib1 = """
import ifoo
@external
def gsf():
pass
@external
@view
def tgeo() -> uint256:
return 1
"""
main = """
import lib1
exports: (lib1.ifoo, lib1.tgeo)
"""
input_bundle = make_input_bundle({"lib1.vy": lib1, "ifoo.vyi": ifoo})
with pytest.raises(StructureException) as e:
compile_code(main, input_bundle=input_bundle)
assert e.value._message == "Methods produce colliding method ID `0x67e43e43`: gsf(), tgeo()"
16 changes: 13 additions & 3 deletions vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,19 @@ def visit_ExportsDecl(self, node):
raise StructureException("not a valid module!", item.value)

module_exposed_fns = {fn.name: fn for fn in module_info.typ.exposed_functions}
funcs = [
module_exposed_fns[f.name] for f in info.typ.functions.values() if f.is_external
]
funcs = []
for f in info.typ.functions.values():
# find the implementation of the function in the specific module
impl = module_exposed_fns.get(f.name)
if impl is None:
msg = f"requested `{item.node_source_code}` but"
msg += f" `{item.value.node_source_code}.{f.name}`"
msg += " is not implemented"
raise StructureException(msg, item)

# guaranteed by `.exposed_functions`:
assert isinstance(impl, ContractFunctionT) and impl.is_external
funcs.append(impl)
else:
raise StructureException(
f"not a function or interface: `{info.typ}`", info.typ.decl_node, item
Expand Down

0 comments on commit 6650768

Please sign in to comment.