summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2016-01-09 17:34:32 -0500
committerNed Batchelder <ned@nedbatchelder.com>2016-01-09 17:34:32 -0500
commitf6d343cf2259491ce9556758c5398e0db76d804c (patch)
tree1849e804fda55277f0ed201be3924e0927f0037f
parentf2beb9c5e97fd887fe0b848812432bf467f30582 (diff)
downloadpython-coveragepy-git-f6d343cf2259491ce9556758c5398e0db76d804c.tar.gz
Properly skip lines that are optimized away
-rw-r--r--coverage/parser.py21
-rw-r--r--tests/test_arcs.py2
2 files changed, 13 insertions, 10 deletions
diff --git a/coverage/parser.py b/coverage/parser.py
index 4f15743a..501b76c4 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -75,7 +75,8 @@ class PythonParser(object):
# Internal detail, used by lab/parser.py.
self.show_tokens = False
- # A dict mapping line numbers to (lo,hi) for multi-line statements.
+ # A dict mapping line numbers to lexical statement starts for
+ # multi-line statements.
self._multiline = {}
# Lazily-created ByteParser and arc data.
@@ -200,11 +201,7 @@ class PythonParser(object):
def first_line(self, line):
"""Return the first line number of the statement including `line`."""
- first_line = self._multiline.get(line)
- if first_line:
- return first_line
- else:
- return line
+ return self._multiline.get(line, line)
def first_lines(self, lines):
"""Map the line numbers in `lines` to the correct first line of the
@@ -257,7 +254,7 @@ class PythonParser(object):
"""
if self._all_arcs is None:
- aaa = AstArcAnalyzer(self.text, self.raw_statements)
+ aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline)
arcs = aaa.collect_arcs()
self._all_arcs = set()
@@ -328,12 +325,15 @@ class AstArcAnalyzer(object):
"""Analyze source text with an AST to find executable code paths."""
@contract(text='unicode', statements=set)
- def __init__(self, text, statements):
+ def __init__(self, text, statements, multiline):
self.root_node = ast.parse(neuter_encoding_declaration(text))
- self.statements = statements
+ # TODO: I think this is happening in too many places.
+ self.statements = set(multiline.get(l, l) for l in statements)
+ self.multiline = multiline
if int(os.environ.get("COVERAGE_ASTDUMP", 0)): # pragma: debugging
# Dump the AST so that failing tests have helpful output.
+ print(self.statements)
ast_dump(self.root_node)
self.arcs = None
@@ -419,6 +419,9 @@ class AstArcAnalyzer(object):
prev_lines = set([from_line])
for body_node in body:
lineno = self.line_for_node(body_node)
+ first_line = self.multiline.get(lineno, lineno)
+ if first_line not in self.statements:
+ continue
for prev_lineno in prev_lines:
self.arcs.add((prev_lineno, lineno))
prev_lines = self.add_arcs(body_node)
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index c8814779..27b8c50b 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -956,7 +956,6 @@ class MiscArcTest(CoverageTest):
)
def test_optimized_away_lines(self):
- self.skip("TODO: fix this test")
self.check_coverage("""\
a = 1
if len([2]):
@@ -968,6 +967,7 @@ class MiscArcTest(CoverageTest):
""",
lines=[1, 2, 3, 7],
arcz=".1 12 23 27 37 7.",
+ arcz_missing="27",
)