summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2019-07-15 09:15:01 (GMT)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-07-15 09:15:01 (GMT)
commit18c5f9d44dde37c0fae5585a604c6027825252d2 (patch)
tree5646a1af289769cc336aa9b4c8441dca80f43ff1 /Lib/test
parentcd6e83b4810549c308ab2d7315dbab526e35ccf6 (diff)
downloadcpython-18c5f9d44dde37c0fae5585a604c6027825252d2.zip
cpython-18c5f9d44dde37c0fae5585a604c6027825252d2.tar.gz
cpython-18c5f9d44dde37c0fae5585a604c6027825252d2.tar.bz2
bpo-37500: Make sure dead code does not generate bytecode but also detect syntax errors (GH-14612)
https://bugs.python.org/issue37500 Add a new field to the compiler structure that allows to be configured so no bytecode is emitted. In this way is possible to detect errors by walking the nodes while preserving optimizations. https://bugs.python.org/issue37500
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_compile.py34
-rw-r--r--Lib/test/test_syntax.py41
-rw-r--r--Lib/test/test_sys_settrace.py48
3 files changed, 110 insertions, 13 deletions
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 56f73f6..9d77f7a 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -697,6 +697,40 @@ if 1:
# complex statements.
compile("if a: b\n" * 200000, "<dummy>", "exec")
+ # Multiple users rely on the fact that CPython does not generate
+ # bytecode for dead code blocks. See bpo-37500 for more context.
+ @support.cpython_only
+ def test_dead_blocks_do_not_generate_bytecode(self):
+ def unused_block_if():
+ if 0:
+ return 42
+
+ def unused_block_while():
+ while 0:
+ return 42
+
+ def unused_block_if_else():
+ if 1:
+ return None
+ else:
+ return 42
+
+ def unused_block_while_else():
+ while 1:
+ return None
+ else:
+ return 42
+
+ funcs = [unused_block_if, unused_block_while,
+ unused_block_if_else, unused_block_while_else]
+
+ for func in funcs:
+ opcodes = list(dis.get_instructions(func))
+ self.assertEqual(2, len(opcodes))
+ self.assertEqual('LOAD_CONST', opcodes[0].opname)
+ self.assertEqual(None, opcodes[0].argval)
+ self.assertEqual('RETURN_VALUE', opcodes[1].opname)
+
class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 8451c07..3829746 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -697,18 +697,47 @@ class SyntaxTestCase(unittest.TestCase):
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")
+ self._check_error("if 0: yield", "outside function")
+ self._check_error("if 0: yield\nelse: x=1", "outside function")
+ self._check_error("if 1: pass\nelse: yield", "outside function")
+ self._check_error("while 0: yield", "outside function")
+ self._check_error("while 0: yield\nelse: x=1", "outside function")
+ self._check_error("class C:\n if 0: yield", "outside function")
+ self._check_error("class C:\n if 1: pass\n else: yield",
+ "outside function")
+ self._check_error("class C:\n while 0: yield", "outside function")
+ self._check_error("class C:\n while 0: yield\n else: x = 1",
+ "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")
+ self._check_error("if 0: return", "outside function")
+ self._check_error("if 0: return\nelse: x=1", "outside function")
+ self._check_error("if 1: pass\nelse: return", "outside function")
+ self._check_error("while 0: return", "outside function")
+ self._check_error("class C:\n if 0: return", "outside function")
+ self._check_error("class C:\n while 0: return", "outside function")
+ self._check_error("class C:\n while 0: return\n else: x=1",
+ "outside function")
+ self._check_error("class C:\n if 0: return\n else: x= 1",
+ "outside function")
+ self._check_error("class C:\n if 1: pass\n else: return",
+ "outside function")
def test_break_outside_loop(self):
- self._check_error("if 0: break", "outside loop")
+ self._check_error("if 0: break", "outside loop")
+ self._check_error("if 0: break\nelse: x=1", "outside loop")
+ self._check_error("if 1: pass\nelse: break", "outside loop")
+ self._check_error("class C:\n if 0: break", "outside loop")
+ self._check_error("class C:\n if 1: pass\n else: break",
+ "outside loop")
def test_continue_outside_loop(self):
- self._check_error("if 0: continue", "not properly in loop")
+ self._check_error("if 0: continue", "not properly in loop")
+ self._check_error("if 0: continue\nelse: x=1", "not properly in loop")
+ self._check_error("if 1: pass\nelse: continue", "not properly in loop")
+ self._check_error("class C:\n if 0: continue", "not properly in loop")
+ self._check_error("class C:\n if 1: pass\n else: continue",
+ "not properly in loop")
def test_unexpected_indent(self):
self._check_error("foo()\n bar()\n", "unexpected indent",
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index 112ea87..fdd7894 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -53,22 +53,52 @@ basic.events = [(0, 'call'),
# following that clause?
-# The entire "while 0:" statement is optimized away. No code
-# exists for it, so the line numbers skip directly from "del x"
-# to "x = 1".
-def arigo_example():
+# Some constructs like "while 0:", "if 0:" or "if 1:...else:..." are optimized
+# away. No code # exists for them, so the line numbers skip directly from
+# "del x" to "x = 1".
+def arigo_example0():
x = 1
del x
while 0:
pass
x = 1
-arigo_example.events = [(0, 'call'),
+arigo_example0.events = [(0, 'call'),
(1, 'line'),
(2, 'line'),
(5, 'line'),
(5, 'return')]
+def arigo_example1():
+ x = 1
+ del x
+ if 0:
+ pass
+ x = 1
+
+arigo_example1.events = [(0, 'call'),
+ (1, 'line'),
+ (2, 'line'),
+ (5, 'line'),
+ (5, 'return')]
+
+def arigo_example2():
+ x = 1
+ del x
+ if 1:
+ x = 1
+ else:
+ pass
+ return None
+
+arigo_example2.events = [(0, 'call'),
+ (1, 'line'),
+ (2, 'line'),
+ (4, 'line'),
+ (7, 'line'),
+ (7, 'return')]
+
+
# check that lines consisting of just one instruction get traced:
def one_instr_line():
x = 1
@@ -349,8 +379,12 @@ class TraceTestCase(unittest.TestCase):
def test_01_basic(self):
self.run_test(basic)
- def test_02_arigo(self):
- self.run_test(arigo_example)
+ def test_02_arigo0(self):
+ self.run_test(arigo_example0)
+ def test_02_arigo1(self):
+ self.run_test(arigo_example1)
+ def test_02_arigo2(self):
+ self.run_test(arigo_example2)
def test_03_one_instr(self):
self.run_test(one_instr_line)
def test_04_no_pop_blocks(self):