Repository: python/mypy

Issue:
Internal error when using reveal_locals() inside class that contains an inner class
While trying to dig deeper on #5914, I ran into an internal error in mypy.

I ran mypy on this code, without any arguments other than the file name:
```python
class Outer:
    class Inner:
        pass
    reveal_locals()
```

This is the traceback: (paths shortened)
```
Traceback (most recent call last):
  File ".../bin/mypy", line 11, in <module>
    sys.exit(console_entry())
  File ".../lib/python3.6/site-packages/mypy/__main__.py", line 7, in console_entry
    main(None)
  File ".../lib/python3.6/site-packages/mypy/main.py", line 92, in main
    res = build.build(sources, options, None, flush_errors, fscache)
  File ".../lib/python3.6/site-packages/mypy/build.py", line 155, in build
    result = _build(sources, options, alt_lib_path, flush_errors, fscache)
  File ".../lib/python3.6/site-packages/mypy/build.py", line 204, in _build
    graph = dispatch(sources, manager)
  File ".../lib/python3.6/site-packages/mypy/build.py", line 2172, in dispatch
    process_graph(graph, manager)
  File ".../lib/python3.6/site-packages/mypy/build.py", line 2469, in process_graph
    process_stale_scc(graph, scc, manager)
  File ".../lib/python3.6/site-packages/mypy/build.py", line 2592, in process_stale_scc
    graph[id].type_check_first_pass()
  File ".../lib/python3.6/site-packages/mypy/build.py", line 1730, in type_check_first_pass
    self.type_checker().check_first_pass()
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 282, in check_first_pass
    self.accept(d)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 393, in accept
    stmt.accept(self)
  File ".../lib/python3.6/site-packages/mypy/nodes.py", line 845, in accept
    return visitor.visit_class_def(self)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 1536, in visit_class_def
    self.accept(defn.defs)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 393, in accept
    stmt.accept(self)
  File ".../lib/python3.6/site-packages/mypy/nodes.py", line 910, in accept
    return visitor.visit_block(self)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 1688, in visit_block
    self.accept(s)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 393, in accept
    stmt.accept(self)
  File ".../lib/python3.6/site-packages/mypy/nodes.py", line 925, in accept
    return visitor.visit_expression_stmt(self)
  File ".../lib/python3.6/site-packages/mypy/checker.py", line 2602, in visit_expression_stmt
    self.expr_checker.accept(s.expr, allow_none_return=True, always_allow_any=True)
  File ".../lib/python3.6/site-packages/mypy/checkexpr.py", line 3003, in accept
    typ = self.visit_call_expr(node, allow_none_return=True)
  File ".../lib/python3.6/site-packages/mypy/checkexpr.py", line 228, in visit_call_expr
    return self.accept(e.analyzed, self.type_context[-1])
  File ".../lib/python3.6/site-packages/mypy/checkexpr.py", line 3007, in accept
    typ = node.accept(self)
  File ".../lib/python3.6/site-packages/mypy/nodes.py", line 1674, in accept
    return visitor.visit_reveal_expr(self)
  File ".../lib/python3.6/site-packages/mypy/checkexpr.py", line 2362, in visit_reveal_expr
    } if expr.local_nodes is not None else {}
  File ".../lib/python3.6/site-packages/mypy/checkexpr.py", line 2361, in <dictcomp>
    var_node.name(): var_node.type for var_node in expr.local_nodes
AttributeError: 'TypeInfo' object has no attribute 'type'
```

I'm using mypy 0.641 on Python 3.6.5.



Discussion:
This is due to improper casting in semanal.py where `local_nodes` is computed. I can quickly fix there.

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2880,23 +2880,24 @@ def visit_call_expr(self, expr: CallExpr) -> None:
                 # Each SymbolTableNode has an attribute node that is nodes.Var
                 # look for variable nodes that marked as is_inferred
                 # Each symboltable node has a Var node as .node
-                local_nodes = cast(
-                    List[Var],
-                    [
-                        n.node for name, n in self.globals.items()
-                        if getattr(n.node, 'is_inferred', False)
-                    ]
-                )
+                local_nodes = [n.node
+                               for name, n in self.globals.items()
+                               if getattr(n.node, 'is_inferred', False)
+                               and isinstance(n.node, Var)]
             elif self.is_class_scope():
                 # type = None  # type: Optional[TypeInfo]
                 if self.type is not None:
-                    local_nodes = cast(List[Var], [st.node for st in self.type.names.values()])
+                    local_nodes = [st.node
+                                   for st in self.type.names.values()
+                                   if isinstance(st.node, Var)]
             elif self.is_func_scope():
                 # locals = None  # type: List[Optional[SymbolTable]]
                 if self.locals is not None:
                     symbol_table = self.locals[-1]
                     if symbol_table is not None:
-                        local_nodes = cast(List[Var], [st.node for st in symbol_table.values()])
+                        local_nodes = [st.node
+                                       for st in symbol_table.values()
+                                       if isinstance(st.node, Var)]
             expr.analyzed = RevealExpr(kind=REVEAL_LOCALS, local_nodes=local_nodes)
             expr.analyzed.line = expr.line
             expr.analyzed.column = expr.column


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -4940,12 +4940,13 @@ class D(B, C):
 class C1(object):
     t = 'a'
     y = 3.0
+    class Inner(object): pass
     reveal_locals()
 
 [out]
-main:4: error: Revealed local types are:
-main:4: error: t: builtins.str
-main:4: error: y: builtins.float
+main:5: error: Revealed local types are:
+main:5: error: t: builtins.str
+main:5: error: y: builtins.float
 
 [case testAbstractClasses]
 import a
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -2301,23 +2301,26 @@ main:2: error: Revealed type is 'def () -> builtins.int'
 [case testRevealLocalsFunction]
 a = 1.0
 
+class A: pass
+
 def f(a: int, b: int) -> int:
     reveal_locals()
     c = a + b
+    class C: pass
     reveal_locals()
     return c
 
 reveal_locals()
 [out]
-main:4: error: Revealed local types are:
-main:4: error: a: builtins.int
-main:4: error: b: builtins.int
 main:6: error: Revealed local types are:
 main:6: error: a: builtins.int
 main:6: error: b: builtins.int
-main:6: error: c: builtins.int
 main:9: error: Revealed local types are:
-main:9: error: a: builtins.float
+main:9: error: a: builtins.int
+main:9: error: b: builtins.int
+main:9: error: c: builtins.int
+main:12: error: Revealed local types are:
+main:12: error: a: builtins.float
 
 [case testNoComplainOverloadNone]
 # flags: --no-strict-optional