Repository: python/mypy

Issue:
Crash when checking for method override
``` py
class A:
    def f(self):
        # type: () -> str
        return 'foo'

class B(A):
    def f(self):
        # type: () -> str
        return self.x

    def initialize(self):
        # type: () -> None
        self.x = 'bar'
```

crashes with

```
/Users/ddfisher/test/test.py:7: error: INTERNAL ERROR -- please report a bug at https://github.com/python/mypy/issues
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/runpy.py", line 184, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/ddfisher/src/mypy/mypy/__main__.py", line 5, in <module>
    main(None)
  File "/Users/ddfisher/src/mypy/mypy/main.py", line 41, in main
    res = type_check_only(sources, bin_dir, options)
  File "/Users/ddfisher/src/mypy/mypy/main.py", line 86, in type_check_only
    options=options)
  File "/Users/ddfisher/src/mypy/mypy/build.py", line 180, in build
    dispatch(sources, manager)
  File "/Users/ddfisher/src/mypy/mypy/build.py", line 1509, in dispatch
    process_graph(graph, manager)
  File "/Users/ddfisher/src/mypy/mypy/build.py", line 1689, in process_graph
    process_stale_scc(graph, scc)
  File "/Users/ddfisher/src/mypy/mypy/build.py", line 1773, in process_stale_scc
    if graph[id].type_check_second_pass():
  File "/Users/ddfisher/src/mypy/mypy/build.py", line 1443, in type_check_second_pass
    return self.type_checker.check_second_pass()
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 211, in check_second_pass
    self.accept(node)
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 237, in accept
    typ = node.accept(self)
  File "/Users/ddfisher/src/mypy/mypy/nodes.py", line 531, in accept
    return visitor.visit_func_def(self)
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 451, in visit_func_def
    self.check_method_override(defn)
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 852, in check_method_override
    self.check_method_or_accessor_override_for_base(defn, base)
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 861, in check_method_or_accessor_override_for_base
    self.check_method_override_for_base_with_name(defn, name, base)
  File "/Users/ddfisher/src/mypy/mypy/checker.py", line 879, in check_method_override_for_base_with_name
    typ = bind_self(self.function_type(defn), self.class_context[-1])
IndexError: list index out of range
```

@elazarg @JukkaL I think this is also related to #2193.



Discussion:

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -65,6 +65,7 @@
     [
         ('node', FuncItem),
         ('context_type_name', Optional[str]),  # Name of the surrounding class (for error messages)
+        ('class_type', Optional[Type]),  # And its type (from class_context)
     ])
 
 
@@ -202,13 +203,20 @@ def check_second_pass(self) -> bool:
         todo = self.deferred_nodes
         self.deferred_nodes = []
         done = set()  # type: Set[FuncItem]
-        for node, type_name in todo:
+        for node, type_name, class_type in todo:
             if node in done:
                 continue
+            # This is useful for debugging:
+            # print("XXX in pass %d, class %s, function %s" %
+            #       (self.pass_num, type_name, node.fullname() or node.name()))
             done.add(node)
             if type_name:
                 self.errors.push_type(type_name)
+            if class_type:
+                self.class_context.append(class_type)
             self.accept(node)
+            if class_type:
+                self.class_context.pop()
             if type_name:
                 self.errors.pop_type()
         return True
@@ -221,7 +229,11 @@ def handle_cannot_determine_type(self, name: str, context: Context) -> None:
                 type_name = self.errors.type_name[-1]
             else:
                 type_name = None
-            self.deferred_nodes.append(DeferredNode(node, type_name))
+            if self.class_context:
+                class_context_top = self.class_context[-1]
+            else:
+                class_context_top = None
+            self.deferred_nodes.append(DeferredNode(node, type_name, class_context_top))
             # Set a marker so that we won't infer additional types in this
             # function. Any inferred types could be bogus, because there's at
             # least one type that we don't know.


Test patch:
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -1296,6 +1296,14 @@ def deco(f: Callable[[T], int]) -> Callable[[T], int]:
 main:1: note: In module imported here:
 tmp/a.py:6: error: Revealed type is 'def (builtins.str*) -> builtins.int'
 
+[case testDeferredClassContext]
+class A:
+    def f(self) -> str: return 'foo'
+class B(A):
+    def f(self) -> str: return self.x
+    def initialize(self): self.x = 'bar'
+[out]
+
 
 -- Scripts and __main__