Repository: python/mypy

Issue:
Better message for invalid types like List(int)
The error message "invalid type comment or annotation" isn't very helpful if a user writes `List(int)` instead of `List[int]` (or `Optional(int)`).  This seems to be a common error.

Example:

```py
from typing import List

def f():
    # type: () -> List(int)   # invalid type comment or annotation
    return [1]
```

A better message could be something like this:

```
program.py:3: error: Syntax error in type annotation
program.py:3:note: Suggestion: Use List[...] instead of List(...)
```


Discussion:
Another example that's not immediately obvious is missing parens around input eg

```
def f(x):
    # type: List[int] -> Tuple[List[int], Optional[str]]
    ...
```
throws message `error: syntax error in type comment`,
but would be nicer if it was more like
```
error: syntax error in type comment
Suggestion: Expected: `(` but found `L` at column 13
```
or 
```
error: syntax error in type comment
Suggestion: Wrap input in parens
```
or something else to highlight the issue
Here's one that took a while to figure out:
```py
def update_state(conf,               # type: SourceConf
                 new_state,          # type: SourceState
                 tid,                # type: int
                 vid,                # type: int
                 update_ts=None,     # type: Optional[float],
                 ):         # type: (...) -> SourceConf
```
This gave `error: Invalid tuple literal type` on the `def` line.

**spoiler alert**

.

.

.

.

.

.

.

.

.

.

.

.

.

There's an extra comma after `Optional[float]`.
@gvanrossum Well, https://github.com/python/mypy/issues/3871 should help a bit in your case. I'd fix it but I'm currently occupied with my PEP.

Resolving patch:
diff --git a/mypy/fastparse.py b/mypy/fastparse.py
--- a/mypy/fastparse.py
+++ b/mypy/fastparse.py
@@ -1015,6 +1015,10 @@ def fail(self, msg: str, line: int, column: int) -> None:
         if self.errors:
             self.errors.report(line, column, msg)
 
+    def note(self, msg: str, line: int, column: int) -> None:
+        if self.errors:
+            self.errors.report(line, column, msg, severity='note')
+
     def visit_raw_str(self, s: str) -> Type:
         # An escape hatch that allows the AST walker in fastparse2 to
         # directly hook into the Python 3.5 type converter in some cases
@@ -1031,12 +1035,19 @@ def translate_expr_list(self, l: Sequence[ast3.expr]) -> List[Type]:
 
     def visit_Call(self, e: ast3.Call) -> Type:
         # Parse the arg constructor
-        if not isinstance(self.parent(), ast3.List):
-            return self.generic_visit(e)
         f = e.func
         constructor = stringify_name(f)
+
+        if not isinstance(self.parent(), ast3.List):
+            self.fail(TYPE_COMMENT_AST_ERROR, self.line, e.col_offset)
+            if constructor:
+                self.note("Suggestion: use {}[...] instead of {}(...)".format(
+                    constructor, constructor),
+                    self.line, e.col_offset)
+            return AnyType(TypeOfAny.from_error)
         if not constructor:
             self.fail("Expected arg constructor name", e.lineno, e.col_offset)
+
         name = None  # type: Optional[str]
         default_type = AnyType(TypeOfAny.special_form)
         typ = default_type  # type: 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
@@ -235,6 +235,30 @@ def f(a):
     pass
 [out]
 main:3: error: invalid type comment or annotation
+main:3: note: Suggestion: use Tuple[...] instead of Tuple(...)
+
+[case testFasterParseTypeErrorList_python2]
+
+from typing import List
+def f(a):
+    # type: (List(int)) -> int
+    pass
+[out]
+main:3: error: invalid type comment or annotation
+main:3: note: Suggestion: use List[...] instead of List(...)
+
+[case testFasterParseTypeErrorCustom]
+
+from typing import TypeVar, Generic
+T = TypeVar('T')
+class Foo(Generic[T]):
+  pass
+
+def f(a: Foo(int)) -> int:
+    pass
+[out]
+main:7: error: invalid type comment or annotation
+main:7: note: Suggestion: use Foo[...] instead of Foo(...)
 
 [case testFastParseMatMul]