Repository: python/mypy

Issue:
Crash when defining function in toplevel while loop
Never mind that this is a terrible practice...
The following code crashes mypy when it is in the top level of a file.

```
while True:
    def foo():
        pass
    foo()
    break
```

Here is the stacktrace:

```
Traceback (most recent call last):
  File "/usr/bin/mypy", line 6, in <module>
    main(__file__)
  File "/usr/lib/python3.5/site-packages/mypy/main.py", line 54, in main
    res = type_check_only(sources, bin_dir, options)
  File "/usr/lib/python3.5/site-packages/mypy/main.py", line 102, in type_check_only
    python_path=options.python_path)
  File "/usr/lib/python3.5/site-packages/mypy/build.py", line 209, in build
    dispatch(sources, manager)
  File "/usr/lib/python3.5/site-packages/mypy/build.py", line 1255, in dispatch
    process_graph(graph, manager)
  File "/usr/lib/python3.5/site-packages/mypy/build.py", line 1386, in process_graph
    process_stale_scc(graph, scc)
  File "/usr/lib/python3.5/site-packages/mypy/build.py", line 1412, in process_stale_scc
    graph[id].semantic_analysis()
  File "/usr/lib/python3.5/site-packages/mypy/build.py", line 1222, in semantic_analysis
    self.manager.semantic_analyzer.visit_file(self.tree, self.xpath)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 241, in visit_file
    self.accept(d)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 2233, in accept
    node.accept(self)
  File "/usr/lib/python3.5/site-packages/mypy/nodes.py", line 783, in accept
    return visitor.visit_while_stmt(self)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 1620, in visit_while_stmt
    s.body.accept(self)
  File "/usr/lib/python3.5/site-packages/mypy/nodes.py", line 715, in accept
    return visitor.visit_block(self)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 1003, in visit_block
    self.accept(s)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 2233, in accept
    node.accept(self)
  File "/usr/lib/python3.5/site-packages/mypy/nodes.py", line 462, in accept
    return visitor.visit_func_def(self)
  File "/usr/lib/python3.5/site-packages/mypy/semanal.py", line 295, in visit_func_def
    if isinstance(symbol.node, FuncDef) and symbol.node != defn:
AttributeError: 'NoneType' object has no attribute 'node'

*** INTERNAL ERROR ***
```



Discussion:

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -2403,9 +2403,16 @@ def visit_import(self, node: Import) -> None:
                 # If the previous symbol is a variable, this should take precedence.
                 self.sem.globals[imported_id] = SymbolTableNode(UNBOUND_IMPORTED, None)
 
+    def visit_while_stmt(self, s: WhileStmt) -> None:
+        s.body.accept(self)
+        if s.else_body:
+            s.else_body.accept(self)
+
     def visit_for_stmt(self, s: ForStmt) -> None:
         self.analyze_lvalue(s.index)
         s.body.accept(self)
+        if s.else_body:
+            s.else_body.accept(self)
 
     def visit_with_stmt(self, s: WithStmt) -> None:
         for n in s.target:


Test patch:
diff --git a/mypy/test/data/check-functions.test b/mypy/test/data/check-functions.test
--- a/mypy/test/data/check-functions.test
+++ b/mypy/test/data/check-functions.test
@@ -1523,9 +1523,20 @@ f = g # E: Incompatible types in assignment (expression has type Callable[[Any,
 [case testFunctionDefinitionWithForStatement]
 for _ in [1]:
     def f(): pass
+else:
+    def g(): pass
 f()
+g()
 [builtins fixtures/list.py]
 
+[case testFunctionDefinitionWithWhileStatement]
+while 1:
+    def f(): pass
+else:
+    def g(): pass
+f()
+g()
+
 [case testBareCallable]
 from typing import Callable, Any