Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix[ux]: include imported structs in interface output #4362

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
64 changes: 64 additions & 0 deletions tests/functional/codegen/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,67 @@ def foo(s: MyStruct) -> MyStruct:
assert "b: uint256" in out
assert "struct Voter:" in out
assert "voted: bool" in out


def test_interface_with_imported_structures(make_input_bundle):
a = """
import b

struct Foo:
val:uint256
"""
b = """
import c

struct Bar:
val:uint256
"""
c = """
struct Baz:
val:uint256
"""
input_bundle = make_input_bundle({"a.vy": a, "b.vy": b, "c.vy": c})
out = compile_code(
a, input_bundle=input_bundle, contract_path="a.vy", output_formats=["interface"]
)["interface"]

assert "# Structs" in out
assert "struct Foo:" in out
assert "struct Bar:" in out
assert "struct Baz" in out


def test_interface_with_doubly_imported_interface(make_input_bundle):
a = """
import b
import c

struct Foo:
val:uint256
"""
b = """
import d

struct Bar:
val:uint256
"""
c = """
import d
struct Baz:
val:uint256
"""
d = """
struct Boo:
val:uint256
"""

input_bundle = make_input_bundle({"a.vy": a, "b.vy": b, "c.vy": c, "d.vy": d})
out = compile_code(
a, input_bundle=input_bundle, contract_path="a.vy", output_formats=["interface"]
)["interface"]

assert "# Structs" in out
assert "struct Foo:" in out
assert "struct Bar:" in out
assert "struct Baz" in out
assert out.count("struct Boo") == 1
23 changes: 19 additions & 4 deletions vyper/compiler/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from vyper.ir import compile_ir
from vyper.semantics.analysis.base import ModuleInfo
from vyper.semantics.types.function import FunctionVisibility, StateMutability
from vyper.semantics.types.module import InterfaceT
from vyper.semantics.types.module import InterfaceT, ModuleT, StructT
Fixed Show fixed Hide fixed
from vyper.typing import StorageLayout
from vyper.utils import vyper_warn
from vyper.warnings import ContractSizeLimitWarning
Expand Down Expand Up @@ -125,12 +125,14 @@ def build_external_interface_output(compiler_data: CompilerData) -> str:


def build_interface_output(compiler_data: CompilerData) -> str:
interface = compiler_data.annotated_vyper_module._metadata["type"].interface
module_t = compiler_data.annotated_vyper_module._metadata["type"]
interface = module_t.interface
out = ""

if len(interface.structs) > 0:
structs = _get_structs(module_t)
if len(structs) > 0:
out += "# Structs\n\n"
for struct in interface.structs.values():
for struct in structs:
out += f"struct {struct.name}:\n"
for member_name, member_type in struct.members.items():
out += f" {member_name}: {member_type}\n"
Expand Down Expand Up @@ -159,6 +161,19 @@ def build_interface_output(compiler_data: CompilerData) -> str:
return out


def _get_structs(m: ModuleT, visited: set[ModuleT] = None) -> [StructT]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use ModuleT.reachable_imports here instead of implementing the recursion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the recursion because for building the import paths for the output - a.b.c. struct.

visited = visited or set()
if m in visited:
return []
visited.add(m)
structs = list(m.interface.structs.values())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. what if two modules have a struct with the same name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that's what i was getting at with #4362 (comment), I'll prepend it.


for val in m.imported_modules.values():
structs += _get_structs(val.module_node._metadata["type"], visited)

return structs


def build_bb_output(compiler_data: CompilerData) -> IRnode:
return compiler_data.venom_functions[0]

Expand Down
Loading