From 09ec74985b1327a901b1344f0442b6436946d5a7 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Wed, 15 May 2019 01:26:07 +0100 Subject: [PATCH] bpo-1875: Raise SyntaxError in invalid blocks that will be optimized away Move the check for dead conditionals (if 0) to the peephole optimizer and make sure that the code block is still compiled to report any existing syntax errors within. --- Lib/test/test_syntax.py | 14 ++++++++++++++ .../2019-05-15-01-29-29.bpo-1875.9oxXFX.rst | 3 +++ Python/compile.c | 9 ++++----- Python/peephole.c | 16 ++++++++++++---- 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-05-15-01-29-29.bpo-1875.9oxXFX.rst diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 4a2899ebca30802..8451c072f64209b 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -696,6 +696,20 @@ def error2(): def test_break_outside_loop(self): self._check_error("break", "outside loop") + def test_yield_outside_function(self): + self._check_error("if 0: yield", "outside function") + self._check_error("class C:\n if 0: yield", "outside function") + + def test_return_outside_function(self): + self._check_error("if 0: return", "outside function") + self._check_error("class C:\n if 0: return", "outside function") + + def test_break_outside_loop(self): + self._check_error("if 0: break", "outside loop") + + def test_continue_outside_loop(self): + self._check_error("if 0: continue", "not properly in loop") + def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", subclass=IndentationError) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-15-01-29-29.bpo-1875.9oxXFX.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-15-01-29-29.bpo-1875.9oxXFX.rst new file mode 100644 index 000000000000000..8f095ed9f16b126 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-15-01-29-29.bpo-1875.9oxXFX.rst @@ -0,0 +1,3 @@ +A :exc:`SyntaxError` is now raised if a code blocks that will be optimized +away (e.g. if conditions that are always false) contains syntax errors. +Patch by Pablo Galindo. diff --git a/Python/compile.c b/Python/compile.c index 91ce04b02e53c95..2a086a509f4595c 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2546,13 +2546,12 @@ compiler_if(struct compiler *c, stmt_ty s) return 0; constant = expr_constant(s->v.If.test); - /* constant = 0: "if 0" + /* constant = 0: "if 0" Leave the optimizations to + * the pephole optimizer to check for syntax errors + * in the block. * constant = 1: "if 1", "if 2", ... * constant = -1: rest */ - if (constant == 0) { - if (s->v.If.orelse) - VISIT_SEQ(c, stmt, s->v.If.orelse); - } else if (constant == 1) { + if (constant == 1) { VISIT_SEQ(c, stmt, s->v.If.body); } else { if (asdl_seq_LEN(s->v.If.orelse)) { diff --git a/Python/peephole.c b/Python/peephole.c index cc244aa433ee186..1ce3535626e9a6b 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -302,11 +302,19 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, case LOAD_CONST: cumlc = lastlc + 1; if (nextop != POP_JUMP_IF_FALSE || - !ISBASICBLOCK(blocks, op_start, i + 1) || - !PyObject_IsTrue(PyList_GET_ITEM(consts, get_arg(codestr, i)))) + !ISBASICBLOCK(blocks, op_start, i + 1)) { break; - fill_nops(codestr, op_start, nexti + 1); - cumlc = 0; + } + PyObject* cnt = PyList_GET_ITEM(consts, get_arg(codestr, i)); + int is_true = PyObject_IsTrue(cnt); + if (is_true == 1) { + fill_nops(codestr, op_start, nexti + 1); + cumlc = 0; + } else if (is_true == 0) { + h = get_arg(codestr, nexti) / sizeof(_Py_CODEUNIT); + tgt = find_op(codestr, codelen, h); + fill_nops(codestr, op_start, tgt); + } break; /* Try to fold tuples of constants.