Repository: vyperlang/vyper

Issue:
Can't import interface using structs
### Version Information

* vyper Version (output of `vyper --version`): 0.2.12+commit.2c6842c
* OS: linux
* Python Version (output of `python --version`): 3.8.5
* Environment (output of `pip freeze`):
```
asttokens==2.0.4
pycryptodome==3.10.1
semantic-version==2.8.5
six==1.15.0
vyper==0.2.12
```

### What's your issue about?

Can't import an interface if it uses structs. Simple example:

foo.vy:
```
struct Widget:
        name: String[8]
        count: uint256

widget: Widget

@external
def show() -> (String[8], uint256):
        return (self.widget.name, self.widget.count)

@external
def __init__():
        self.widget = Widget({
                name: "thing",
                count: 1
        })
```
bar.vy
```
import foo as Foo

@external
def __init__():
        pass
```

Throw both in the same dir.

`vyper foo.vy` results in a successful compilation

`vyper bar.vy` results in:
```
Error compiling: bar.vy
vyper.exceptions.InvalidType: Invalid base type: Widget
  contract "Foo", line 5:8 
       4
  ---> 5 widget: Widget
  ---------------^
       6
```

### How can it be fixed?

Haven't spent time fixing yet


Discussion:
At first I thought it was because `widget` was public, but this is indeed a bug with how our current processing works

Resolving patch:
diff --git a/vyper/ast/signatures/interface.py b/vyper/ast/signatures/interface.py
--- a/vyper/ast/signatures/interface.py
+++ b/vyper/ast/signatures/interface.py
@@ -99,8 +99,20 @@ def extract_sigs(sig_code, interface_name=None):
         interface_ast = [
             i
             for i in vy_ast.parse_to_ast(sig_code["code"], contract_name=interface_name)
-            if isinstance(i, vy_ast.FunctionDef)
-            or isinstance(i, vy_ast.EventDef)
+            # all the nodes visited by ModuleNodeVisitor.
+            if isinstance(
+                i,
+                (
+                    vy_ast.FunctionDef,
+                    vy_ast.EventDef,
+                    vy_ast.StructDef,
+                    vy_ast.InterfaceDef,
+                    # parsing import statements at this stage
+                    # causes issues with recursive imports
+                    # vy_ast.Import,
+                    # vy_ast.ImportFrom,
+                ),
+            )
             or (isinstance(i, vy_ast.AnnAssign) and i.target.id != "implements")
         ]
         global_ctx = GlobalContext.get_global_context(interface_ast)


Test patch:
diff --git a/tests/cli/vyper_compile/test_import_paths.py b/tests/cli/vyper_compile/test_import_paths.py
--- a/tests/cli/vyper_compile/test_import_paths.py
+++ b/tests/cli/vyper_compile/test_import_paths.py
@@ -5,19 +5,24 @@
 FOO_CODE = """
 {}
 
+struct FooStruct:
+    foo_: uint256
+
 @external
-def foo() -> uint256:
-    return 13
+def foo() -> FooStruct:
+    return FooStruct({{foo_: 13}})
 
 @external
-def bar(a: address) -> uint256:
+def bar(a: address) -> FooStruct:
     return {}(a).bar()
 """
 
 BAR_CODE = """
+struct FooStruct:
+    foo_: uint256
 @external
-def bar() -> uint256:
-    return 13
+def bar() -> FooStruct:
+    return FooStruct({foo_: 13})
 """
 
 
@@ -127,17 +132,20 @@ def test_import_parent_folder(tmp_path, assert_compile_failed):
 
 @pytest.mark.parametrize("import_stmt", META_IMPORT_STMT)
 def test_import_self_interface(import_stmt, tmp_path):
-    # a contract can access it's derived interface by importing itself
+    # a contract can access its derived interface by importing itself
     code = f"""
 {import_stmt}
 
+struct FooStruct:
+    foo_: uint256
+
 @external
-def know_thyself(a: address) -> uint256:
+def know_thyself(a: address) -> FooStruct:
     return Meta(a).be_known()
 
 @external
-def be_known() -> uint256:
-    return 42
+def be_known() -> FooStruct:
+    return FooStruct({{foo_: 42}})
     """
 
     tmp_path.joinpath("contracts").mkdir()
@@ -167,12 +175,15 @@ def test_derived_interface_imports(import_stmt_baz, import_stmt_foo, tmp_path):
     baz_code = f"""
 {import_stmt_baz}
 
+struct FooStruct:
+    foo_: uint256
+
 @external
-def foo(a: address) -> uint256:
+def foo(a: address) -> FooStruct:
     return Foo(a).foo()
 
 @external
-def bar(_foo: address, _bar: address) -> uint256:
+def bar(_foo: address, _bar: address) -> FooStruct:
     return Foo(_foo).bar(_bar)
     """
 
@@ -198,9 +209,15 @@ def test_local_namespace(tmp_path):
         "import foo as BarFoo",
         "import bar as BarFoo",
     ]
+    struct_def = """
+struct FooStruct:
+    foo_: uint256
+
+    """
 
     compile_paths = []
     for i, code in enumerate(codes):
+        code += struct_def
         path = tmp_path.joinpath(f"code{i}.vy")
         with path.open("w") as fp:
             fp.write(code)