Repository: python/mypy

Issue:
No error for bare asterisk after *args
The following code is accepted by the parser, even though Python gives a syntax error:

```
def f(*x, *, y = 1): pass
```

The code should generate a parse error.



Discussion:
I'll take this

This is still up for grabs.


Resolving patch:
diff --git a/mypy/parse.py b/mypy/parse.py
--- a/mypy/parse.py
+++ b/mypy/parse.py
@@ -502,13 +502,22 @@ def parse_arg_list(
         assigns = []   # type: List[Token]
         
         require_named = False
+        bare_asterisk_before = -1
         
         if self.current_str() != ')' and self.current_str() != ':':
             while self.current_str() != ')':
                 if self.current_str() == '*' and self.peek().string == ',':
+                    if require_named:
+                        # can only have one bare star, must be before any
+                        # *args or **args
+                        self.parse_error()
                     self.expect('*')
                     require_named = True
+                    bare_asterisk_before = len(args)
                 elif self.current_str() in ['*', '**']:
+                    if bare_asterisk_before == len(args):
+                        # named arguments must follow bare *
+                        self.parse_error()
                     asterisk.append(self.skip())
                     isdict = asterisk[-1].string == '**'
                     name = self.expect_type(Name)
@@ -540,11 +549,13 @@ def parse_arg_list(
                         else:
                             kinds.append(nodes.ARG_OPT)
                     else:
-                        if require_named:
-                            self.parse_error()
                         init.append(None)
                         assigns.append(none)
-                        kinds.append(nodes.ARG_POS)
+                        if require_named:
+                            # required keyword-only argument
+                            kinds.append(nodes.ARG_NAMED)
+                        else:
+                            kinds.append(nodes.ARG_POS)
                         
                 if self.current().string != ',':
                     break
diff --git a/stubs/2.7/builtins.py b/stubs/2.7/builtins.py
--- a/stubs/2.7/builtins.py
+++ b/stubs/2.7/builtins.py
@@ -99,7 +99,6 @@ def __rxor__(self, n: int) -> int: pass
     def __rlshift__(self, n: int) -> int: pass
     def __rrshift__(self, n: int) -> int: pass
 
-    def __pos__(self) -> int: pass
     def __neg__(self) -> int: pass
     def __pos__(self) -> int: pass
     def __invert__(self) -> int: pass
@@ -157,7 +156,6 @@ def __lt__(self, x: float) -> bool: pass
     def __le__(self, x: float) -> bool: pass
     def __gt__(self, x: float) -> bool: pass
     def __ge__(self, x: float) -> bool: pass
-    def __pos__(self) -> float: pass
     def __neg__(self) -> float: pass
     def __pos__(self) -> float: pass
 
@@ -936,7 +934,7 @@ def ord(c: bytearray) -> int: pass
 
 # This is only available after from __future__ import print_function.
 # TODO: Use IO[str] instead of TextIO (once IO is available)?
-def print(*values: Any, *, sep: unicode = u' ', end: unicode = u'\n',
+def print(*values: Any, sep: unicode = u' ', end: unicode = u'\n',
            file: TextIO = None) -> None: pass
 
 # The return type can be int or float, depending on the value of y.
diff --git a/stubs/3.2/builtins.py b/stubs/3.2/builtins.py
--- a/stubs/3.2/builtins.py
+++ b/stubs/3.2/builtins.py
@@ -1027,7 +1027,7 @@ def ord(c: bytes) -> int: pass
 @overload
 def ord(c: bytearray) -> int: pass
 
-def print(*values: Any, *, sep: str = ' ', end: str = '\n',
+def print(*values: Any, sep: str = ' ', end: str = '\n',
            file: IO[str] = None) -> None: pass
 
 # The return type can be int or float, depending on the value of y.


Test patch:
diff --git a/mypy/test/data/parse-errors.test b/mypy/test/data/parse-errors.test
--- a/mypy/test/data/parse-errors.test
+++ b/mypy/test/data/parse-errors.test
@@ -100,31 +100,60 @@ f(*x, y)
 [out]
 file, line 1: Parse error before "y"
 
-[case testInvalidFuncDefArgs1]
-def f(x = y, x): pass
+[case testInvalidBareAsteriskAndVarArgs1]
+def f(*x: A, *, y: B = 1) -> None: pass
 [out]
 file: In function "f":
-file, line 1: Invalid argument list
+file, line 1: Parse error before *
+file, line 1: Parse error before end of line
 
-[case testInvalidFuncDefArgs2]
-def f(*x, y): 
-    pass
+[case testInvalidBareAsteriskAndVarArgs2]
+def f(*x: A, *) -> None: pass
 [out]
 file: In function "f":
 file, line 1: Parse error before )
-file: At top level:
-file, line 1: Parse error before :
-file, line 2: Inconsistent indentation
+file, line 1: Parse error before end of line
+
+[case testInvalidBareAsteriskAndVarArgs3]
+def f(*, *x: A) -> None: pass
+[out]
+file: In function "f":
+file, line 1: Parse error before *
+file, line 1: Parse error before end of line
+
+[case testInvalidBareAsteriskAndVarArgs4]
+def f(*, **x: A) -> None: pass
+[out]
+file: In function "f":
+file, line 1: Parse error before **
+file, line 1: Parse error before end of line
+
+[case testInvalidBareAsterisk1]
+def f(*) -> None: pass
+[out]
+file: In function "f":
+file, line 1: Parse error before )
+file, line 1: Parse error before end of line
+
+[case testInvalidBareAsterisk2]
+def f(x, *) -> None: pass
+[out]
+file: In function "f":
+file, line 1: Parse error before )
+file, line 1: Parse error before end of line
+
+[case testInvalidFuncDefArgs1]
+def f(x = y, x): pass
+[out]
+file: In function "f":
+file, line 1: Invalid argument list
 
 [case testInvalidFuncDefArgs3]
-def f(**x, y): 
+def f(**x, y):
    pass
 [out]
 file: In function "f":
-file, line 1: Parse error before )
-file: At top level:
-file, line 1: Parse error before :
-file, line 2: Inconsistent indentation
+file, line 1: Invalid argument list
 
 [case testInvalidFuncDefArgs4]
 def f(**x, y=x): 
diff --git a/mypy/test/data/parse.test b/mypy/test/data/parse.test
--- a/mypy/test/data/parse.test
+++ b/mypy/test/data/parse.test
@@ -2137,8 +2137,8 @@ MypyFile:1(
     Block:1(
       PassStmt:1())))
 
-[case testBareAsteriskAndVarArgs]
-def f(*x: A, *, y: B = 1) -> None: pass
+[case testBareAsteriskNamedDefault]
+def f(*, y: B = 1) -> None: pass
 [out]
 MypyFile:1(
   FuncDef:1(
@@ -2146,13 +2146,25 @@ MypyFile:1(
     MaxPos(0)
     Args(
       Var(y))
-    def (*x: A?, *, y: B?) -> None?
+    def (*, y: B?) -> None?
     Init(
       AssignmentStmt:1(
         NameExpr(y)
         IntExpr(1)))
-    VarArg(
-      Var(x))
+    Block:1(
+      PassStmt:1())))
+
+[case testBareAsteriskNamedNoDefault]
+def f(*, y: B) -> None: pass
+[out]
+MypyFile:1(
+  FuncDef:1(
+    f
+    MaxPos(0)
+    Args(
+      Var(y))
+    def (*, y: B?) -> None?
+    Init()
     Block:1(
       PassStmt:1())))