Repository: xonsh/xonsh

Issue:
Wrong error with foo(*)
With the input:

```
def spam(*): pass
```

I get the confusing:

```
TypeError: 'NoneType' object is not iterable
```

Instead of 

```
SyntaxError: named arguments must follow bare *
```

Backtrace:

```
xonsh: To log full traceback to a file set: $XONSH_TRACEBACK_LOGFILE = <filename>
Traceback (most recent call last):
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/ptk/shell.py", line 137, in _push
    locs=None)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 19877, in compile
    transform=transform)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 19846, in parse
    tree, input = self._parse_ctx_free(input, mode=mode, filename=filename)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/__amalgam__.py", line 19933, in _parse_ctx_free
    debug_level=(self.debug_level > 2))
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/parsers/base.py", line 349, in parse
    tree = self.parser.parse(input=s, lexer=self.lexer, debug=debug_level)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/ply/yacc.py", line 331, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/ply/yacc.py", line 1118, in parseopt_notrack
    p.callable(pslice)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/parsers/base.py", line 653, in p_typedargslist_times4
    self._set_var_args(p0, p[2], None)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/parsers/base.py", line 762, in _set_var_args
    self._set_args_def(p0, kwargs, kwargs=True)
  File "/home/jbliss/.conda/lib/python3.6/site-packages/xonsh/parsers/base.py", line 736, in _set_args_def
    for v in vals:
TypeError: 'NoneType' object is not iterable
```


Discussion:

Resolving patch:
diff --git a/xonsh/parsers/base.py b/xonsh/parsers/base.py
--- a/xonsh/parsers/base.py
+++ b/xonsh/parsers/base.py
@@ -646,21 +646,37 @@ def p_typedargslist_kwarg(self, p):
         p[0] = ast.arguments(args=[], vararg=None, kwonlyargs=[],
                              kw_defaults=[], kwarg=p[2], defaults=[])
 
-    def p_typedargslist_times4(self, p):
-        """typedargslist : TIMES tfpdef_opt comma_pow_tfpdef_opt"""
+    def p_typedargslist_times4_tfpdef(self, p):
+        """typedargslist : TIMES tfpdef comma_pow_tfpdef_opt"""
+        # *args, **kwargs
         p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
                            kwarg=p[3], defaults=[])
         self._set_var_args(p0, p[2], None)
         p[0] = p0
 
-    def p_typedargslist_times5(self, p):
-        """typedargslist : TIMES tfpdef_opt comma_tfpdef_list comma_pow_tfpdef_opt"""
+    def p_typedargslist_times4_comma(self, p):
+        """typedargslist : TIMES comma_pow_tfpdef"""
+        # *, **kwargs
+        p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
+                           kwarg=p[2], defaults=[])
+        p[0] = p0
+
+    def p_typedargslist_times5_tdpdef(self, p):
+        """typedargslist : TIMES tfpdef comma_tfpdef_list comma_pow_tfpdef_opt"""
         # *args, x, **kwargs
         p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
                            kwarg=p[4], defaults=[])
         self._set_var_args(p0, p[2], p[3])  # *args
         p[0] = p0
 
+    def p_typedargslist_times5_comma(self, p):
+        """typedargslist : TIMES comma_tfpdef_list comma_pow_tfpdef_opt"""
+        # *, x, **kwargs
+        p0 = ast.arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[],
+                           kwarg=p[3], defaults=[])
+        self._set_var_args(p0, None, p[2])  # *args
+        p[0] = p0
+
     def p_typedargslist_t5(self, p):
         """typedargslist : tfpdef equals_test_opt comma_tfpdef_list_opt comma_opt"""
         # x
@@ -733,6 +749,9 @@ def p_comma_pow_tfpdef(self, p):
     def _set_args_def(self, argmts, vals, kwargs=False):
         args, defs = (argmts.kwonlyargs, argmts.kw_defaults) if kwargs else \
                      (argmts.args, argmts.defaults)
+        if vals is None and kwargs:
+            loc = self.currloc(self.lineno, self.col)
+            self._parse_error('named arguments must follow bare *', loc)
         for v in vals:
             args.append(v['arg'])
             d = v['default']
@@ -758,7 +777,7 @@ def _set_regular_args(self, p0, p1, p2, p3, p4):
             self._set_args_def(p0, p3)
 
     def _set_var_args(self, p0, vararg, kwargs):
-        if vararg is None:
+        if vararg is None and kwargs is not None:
             self._set_args_def(p0, kwargs, kwargs=True)
         elif vararg is not None and kwargs is None:
             # *args


Test patch:
diff --git a/tests/test_parser.py b/tests/test_parser.py
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -2310,3 +2310,8 @@ def test_syntax_error_augassign_ops(exp):
 def test_syntax_error_augassign_cmp(exp):
     with pytest.raises(SyntaxError):
         PARSER.parse('{} += a'.format(exp))
+
+
+def test_syntax_error_bar_kwonlyargs():
+    with pytest.raises(SyntaxError):
+        PARSER.parse('def spam(*):\n   pass\n', mode='exec')