Repository: python/mypy

Issue:
Crash on incorrect Callable syntax
On this file
```
x: Callable[[x]. x]
```
mypy crashes with the following traceback:
```
Traceback (most recent call last):
  File "/usr/local/bin/mypy", line 6, in <module>
    main(__file__)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 42, in main
    res = type_check_only(sources, bin_dir, options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/main.py", line 86, in type_check_only
    options=options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 183, in build
    dispatch(sources, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1523, in dispatch
    graph = load_graph(sources, manager)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1605, in load_graph
    root_source=True)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1212, in __init__
    self.parse_file()
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1376, in parse_file
    self.ignore_all or self.options.ignore_errors)
  File "/usr/lib/python3.4/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1293, in wrap_context
    yield
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 1376, in parse_file
    self.ignore_all or self.options.ignore_errors)
  File "/usr/local/lib/python3.4/dist-packages/mypy/build.py", line 462, in parse_file
    tree = parse(source, path, self.errors, options=self.options)
  File "/usr/local/lib/python3.4/dist-packages/mypy/parse.py", line 96, in parse
    custom_typing_module=options.custom_typing_module)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 74, in parse
    ).visit(ast)
  File "/usr/local/lib/python3.4/dist-packages/typed_ast-0.6.3-py3.4-linux-x86_64.egg/typed_ast/ast35.py", line 256, in visit
    return visitor(node)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 257, in visit_Module
    body = self.fix_function_overloads(self.translate_stmt_list(mod.body))
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 157, in translate_stmt_list
    stmt = self.visit(e)
  File "/usr/local/lib/python3.4/dist-packages/typed_ast-0.6.3-py3.4-linux-x86_64.egg/typed_ast/ast35.py", line 256, in visit
    return visitor(node)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 101, in wrapper
    node = f(self, ast)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 502, in visit_Assign
    typ = TypeConverter(self.errors, line=n.lineno).visit(n.annotation)  # type: ignore
  File "/usr/local/lib/python3.4/dist-packages/typed_ast-0.6.3-py3.4-linux-x86_64.egg/typed_ast/ast35.py", line 256, in visit
    return visitor(node)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 968, in visit_Subscript
    params = [self.visit(n.slice.value)]
  File "/usr/local/lib/python3.4/dist-packages/typed_ast-0.6.3-py3.4-linux-x86_64.egg/typed_ast/ast35.py", line 256, in visit
    return visitor(node)
  File "/usr/local/lib/python3.4/dist-packages/mypy/fastparse.py", line 979, in visit_Attribute
    assert isinstance(before_dot, UnboundType)
AssertionError: 
```


Discussion:
Confirmed. It's not limited to PEP 526 annotations, the following also crashes:
```python
y = None  # type: Callable[[x].x]
```
and so does this:
```python
def f(y: Callable[[x].x]): pass
```
It also crashes in Python 2 mode.
It looks like it is not limited to ``Callable``  either, these two also crash:
```python
x: Tuple[x].x
x: Iterable[x].x
```
As well it is not limited to attribute, subscript also fails:
```python
x: Tuple[x][x]
x: Iterable[x][x]
x: Callable[x][x]
```
However, it looks like there is a simple fix.

Resolving patch:
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -44,7 +44,7 @@
 V = TypeVar('V')
 
 TYPE_COMMENT_SYNTAX_ERROR = 'syntax error in type comment'
-TYPE_COMMENT_AST_ERROR = 'invalid type comment'
+TYPE_COMMENT_AST_ERROR = 'invalid type comment or annotation'
 
 
 def parse(source: Union[str, bytes], fnam: str = None, errors: Errors = None,
@@ -954,11 +954,6 @@ def visit_Subscript(self, n: ast35.Subscript) -> Type:
             self.fail(TYPE_COMMENT_SYNTAX_ERROR, self.line, getattr(n, 'col_offset', -1))
             return AnyType()
 
-        value = self.visit(n.value)
-
-        assert isinstance(value, UnboundType)
-        assert not value.args
-
         empty_tuple_index = False
         if isinstance(n.slice.value, ast35.Tuple):
             params = self.translate_expr_list(n.slice.value.elts)
@@ -967,7 +962,13 @@ def visit_Subscript(self, n: ast35.Subscript) -> Type:
         else:
             params = [self.visit(n.slice.value)]
 
-        return UnboundType(value.name, params, line=self.line, empty_tuple_index=empty_tuple_index)
+        value = self.visit(n.value)
+        if isinstance(value, UnboundType) and not value.args:
+            return UnboundType(value.name, params, line=self.line,
+                               empty_tuple_index=empty_tuple_index)
+        else:
+            self.fail(TYPE_COMMENT_AST_ERROR, self.line, getattr(n, 'col_offset', -1))
+            return AnyType()
 
     def visit_Tuple(self, n: ast35.Tuple) -> Type:
         return TupleType(self.translate_expr_list(n.elts), None, implicit=True, line=self.line)
@@ -976,10 +977,11 @@ def visit_Tuple(self, n: ast35.Tuple) -> Type:
     def visit_Attribute(self, n: ast35.Attribute) -> Type:
         before_dot = self.visit(n.value)
 
-        assert isinstance(before_dot, UnboundType)
-        assert not before_dot.args
-
-        return UnboundType("{}.{}".format(before_dot.name, n.attr), line=self.line)
+        if isinstance(before_dot, UnboundType) and not before_dot.args:
+            return UnboundType("{}.{}".format(before_dot.name, n.attr), line=self.line)
+        else:
+            self.fail(TYPE_COMMENT_AST_ERROR, self.line, getattr(n, 'col_offset', -1))
+            return AnyType()
 
     # Ellipsis
     def visit_Ellipsis(self, n: ast35.Ellipsis) -> Type:


Test patch:
diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test
--- a/test-data/unit/check-fastparse.test
+++ b/test-data/unit/check-fastparse.test
@@ -8,7 +8,7 @@ x = None # type: a : b  # E: syntax error in type comment
 
 [case testFastParseInvalidTypeComment]
 # flags: --fast-parser
-x = None # type: a + b  # E: invalid type comment
+x = None # type: a + b  # E: invalid type comment or annotation
 
 -- Function type comments are attributed to the function def line.
 -- This happens in both parsers.
@@ -20,10 +20,75 @@ def f():  # E: syntax error in type comment
 
 [case testFastParseInvalidFunctionAnnotation]
 # flags: --fast-parser
-def f(x):  # E: invalid type comment
+def f(x):  # E: invalid type comment or annotation
   # type: (a + b) -> None
   pass
 
+[case testFastParseInvalidTypes2]
+# flags: --fast-parser --py2
+# All of these should not crash
+from typing import Callable, Tuple, Iterable
+
+x = None # type: Tuple[int, str].x # E: invalid type comment or annotation
+x = None # type: Iterable[x].x # E: invalid type comment or annotation
+x = None # type: Tuple[x][x] # E: invalid type comment or annotation
+x = None # type: Iterable[x][x] # E: invalid type comment or annotation
+x = None # type: Callable[..., int][x] # E: invalid type comment or annotation
+x = None # type: Callable[..., int].x # E: invalid type comment or annotation
+x = None # type: Tuple[1] # E: invalid type comment or annotation
+
+def f1(x): # E: invalid type comment or annotation
+    # type: (Tuple[int, str].x) -> None
+    pass
+def f2(x): # E: invalid type comment or annotation
+    # type: (Iterable[x].x) -> None
+    pass
+def f3(x): # E: invalid type comment or annotation
+    # type: (Tuple[x][x]) -> None
+    pass
+def f4(x): # E: invalid type comment or annotation
+    # type: (Iterable[x][x]) -> None
+    pass
+def f5(x): # E: invalid type comment or annotation
+    # type: (Callable[..., int][x]) -> None
+    pass
+def f6(x): # E: invalid type comment or annotation
+    # type: (Callable[..., int].x) -> None
+    pass
+def f7(x): # E: invalid type comment or annotation
+    # type: (Tuple[1]) -> None
+    pass
+
+
+[case testFastParseInvalidTypes3]
+# flags: --fast-parser --python-version 3.6
+# All of these should not crash
+from typing import Callable, Tuple, Iterable
+
+x: Tuple[int, str].x # E: invalid type comment or annotation
+x: Iterable[x].x # E: invalid type comment or annotation
+x: Tuple[x][x] # E: invalid type comment or annotation
+x: Iterable[x][x] # E: invalid type comment or annotation
+x: Callable[..., int][x] # E: invalid type comment or annotation
+x: Callable[..., int].x # E: invalid type comment or annotation
+x: Tuple[1] # E: invalid type comment or annotation
+
+x = None # type: Tuple[int, str].x # E: invalid type comment or annotation
+x = None # type: Iterable[x].x # E: invalid type comment or annotation
+x = None # type: Tuple[x][x] # E: invalid type comment or annotation
+x = None # type: Iterable[x][x] # E: invalid type comment or annotation
+x = None # type: Callable[..., int][x] # E: invalid type comment or annotation
+x = None # type: Callable[..., int].x # E: invalid type comment or annotation
+x = None # type: Tuple[1] # E: invalid type comment or annotation
+
+def f1(x: Tuple[int, str].x) -> None: pass # E: invalid type comment or annotation
+def f2(x: Iterable[x].x) -> None: pass # E: invalid type comment or annotation
+def f3(x: Tuple[x][x]) -> None: pass # E: invalid type comment or annotation
+def f4(x: Iterable[x][x]) -> None: pass # E: invalid type comment or annotation
+def f5(x: Callable[..., int][x]) -> None: pass # E: invalid type comment or annotation
+def f6(x: Callable[..., int].x) -> None: pass # E: invalid type comment or annotation
+def f7(x: Tuple[1]) -> None: pass # E: invalid type comment or annotation
+
 [case testFastParseProperty]
 # flags: --fast-parser
 class C:
@@ -169,7 +234,7 @@ def f(a):
     # type: (Tuple(int, int)) -> int
     pass
 [out]
-main:3: error: invalid type comment
+main:3: error: invalid type comment or annotation
 
 [case testFastParseMatMul]
 # flags: --fast-parser