From ac43bebccc5a88e7a9b31e9ca5c5cbdc46f0fc5a Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Tue, 22 Oct 2024 09:29:03 -0400 Subject: [PATCH] reject weird exports of value types also add a test for an exception case that was not tested before --- .../functional/syntax/modules/test_exports.py | 33 +++++++++++++++++++ vyper/semantics/analysis/module.py | 10 +++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/functional/syntax/modules/test_exports.py b/tests/functional/syntax/modules/test_exports.py index def1849d58..aa92118d20 100644 --- a/tests/functional/syntax/modules/test_exports.py +++ b/tests/functional/syntax/modules/test_exports.py @@ -485,3 +485,36 @@ def an_internal_function(): # as_posix() for windows lib1_path = (tmp_path / "lib1.vy").as_posix() assert e.value._message == f"lib1 (located at `{lib1_path}`) has no external functions!" + + +def test_invalid_export(make_input_bundle): + lib1 = """ +@external +def foo(): + pass + """ + main = """ +import lib1 +a: address + +exports: lib1.__interface__(self.a).foo + """ + 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 == "invalid export of a value" + assert e.value._hint == "exports should look like ." + + main = """ +interface Foo: + def foo(): nonpayable + +exports: Foo + """ + with pytest.raises(StructureException) as e: + compile_code(main) + + assert e.value._message == "invalid export" + assert e.value._hint == "exports should look like ." diff --git a/vyper/semantics/analysis/module.py b/vyper/semantics/analysis/module.py index 34e1624655..737f675b7c 100644 --- a/vyper/semantics/analysis/module.py +++ b/vyper/semantics/analysis/module.py @@ -40,7 +40,7 @@ ) from vyper.semantics.data_locations import DataLocation from vyper.semantics.namespace import Namespace, get_namespace, override_global_namespace -from vyper.semantics.types import EventT, FlagT, InterfaceT, StructT, is_type_t +from vyper.semantics.types import TYPE_T, EventT, FlagT, InterfaceT, StructT, is_type_t from vyper.semantics.types.function import ContractFunctionT from vyper.semantics.types.module import ModuleT from vyper.semantics.types.utils import type_from_annotation @@ -499,6 +499,14 @@ def visit_ExportsDecl(self, node): raise StructureException("not a public variable!", decl, item) funcs = [decl._expanded_getter._metadata["func_type"]] elif isinstance(info.typ, ContractFunctionT): + # e.g. lib1.__interface__(self._addr).foo + if not isinstance(get_expr_info(item.value).typ, (ModuleT, TYPE_T)): + raise StructureException( + "invalid export of a value", + item.value, + hint="exports should look like .", + ) + # regular function funcs = [info.typ] elif is_type_t(info.typ, InterfaceT):