From 9a56912ad16065c8fc3670c8d493f922bc54e5b1 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Mon, 23 Nov 2015 16:50:42 +0000
Subject: [PATCH] py/compile: Do proper checking of * and ** in function
 definition.

This patch checks that there is only one *, and that ** is last in the
arg list.
---
 py/compile.c                | 12 +++++++++++-
 py/grammar.h                |  4 ++--
 tests/basics/syntaxerror.py |  8 ++++++++
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/py/compile.c b/py/compile.c
index e15e7b3b8..4fcd64f29 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -2505,7 +2505,12 @@ STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) {
 }
 
 STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star) {
-    // TODO verify that *k and **k are last etc
+    // check that **kw is last
+    if ((comp->scope_cur->scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
+        compile_syntax_error(comp, pn, "invalid syntax");
+        return;
+    }
+
     qstr param_name = MP_QSTR_NULL;
     uint param_flag = ID_FLAG_IS_PARAM;
     if (MP_PARSE_NODE_IS_ID(pn)) {
@@ -2530,6 +2535,11 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
                 comp->scope_cur->num_pos_args += 1;
             }
         } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
+            if (comp->have_star) {
+                // more than one star
+                compile_syntax_error(comp, pn, "invalid syntax");
+                return;
+            }
             comp->have_star = true;
             param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_STAR_PARAM;
             if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
diff --git a/py/grammar.h b/py/grammar.h
index b507132d0..b7036c8ec 100644
--- a/py/grammar.h
+++ b/py/grammar.h
@@ -60,7 +60,7 @@ DEF_RULE(decorated, c(decorated), and(2), rule(decorators), rule(decorated_body)
 DEF_RULE(decorated_body, nc, or(2), rule(classdef), rule(funcdef))
 DEF_RULE(funcdef, c(funcdef), blank | and(8), tok(KW_DEF), tok(NAME), tok(DEL_PAREN_OPEN), opt_rule(typedargslist), tok(DEL_PAREN_CLOSE), opt_rule(funcdefrettype), tok(DEL_COLON), rule(suite))
 DEF_RULE(funcdefrettype, nc, ident | and(2), tok(DEL_MINUS_MORE), rule(test))
-// TODO typedargslist lets through more than is allowed
+// note: typedargslist lets through more than is allowed, compiler does further checks
 DEF_RULE(typedargslist, nc, list_with_end, rule(typedargslist_item), tok(DEL_COMMA))
 DEF_RULE(typedargslist_item, nc, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star))
 DEF_RULE(typedargslist_name, nc, ident | and(3), tok(NAME), opt_rule(typedargslist_colon), opt_rule(typedargslist_equal))
@@ -69,7 +69,7 @@ DEF_RULE(typedargslist_dbl_star, nc, and(3), tok(OP_DBL_STAR), tok(NAME), opt_ru
 DEF_RULE(typedargslist_colon, nc, ident | and(2), tok(DEL_COLON), rule(test))
 DEF_RULE(typedargslist_equal, nc, ident | and(2), tok(DEL_EQUAL), rule(test))
 DEF_RULE(tfpdef, nc, and(2), tok(NAME), opt_rule(typedargslist_colon))
-// TODO varargslist lets through more than is allowed
+// note: varargslist lets through more than is allowed, compiler does further checks
 DEF_RULE(varargslist, nc, list_with_end, rule(varargslist_item), tok(DEL_COMMA))
 DEF_RULE(varargslist_item, nc, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star))
 DEF_RULE(varargslist_name, nc, ident | and(2), tok(NAME), opt_rule(varargslist_equal))
diff --git a/tests/basics/syntaxerror.py b/tests/basics/syntaxerror.py
index 24c3fe6e4..2ae0183f8 100644
--- a/tests/basics/syntaxerror.py
+++ b/tests/basics/syntaxerror.py
@@ -113,3 +113,11 @@ test_syntax('def f(x):\n nonlocal x')
 
 # can define variable to be both nonlocal and global
 test_syntax('def f():\n nonlocal x\n global x')
+
+# can't have multiple *'s
+test_syntax('def f(x, *a, *):\n pass')
+test_syntax('lambda x, *a, *: 1')
+
+# **kw must be last
+test_syntax('def f(x, *a, **kw, r):\n pass')
+test_syntax('lambda x, *a, **kw, r: 1')
-- 
GitLab