diff --git a/CHANGES.rst b/CHANGES.rst index 3b1337d2d..7bddd369f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,11 +23,17 @@ upgrading your version of coverage.py. Unreleased ---------- -- fix: the LCOV report code assumed that a branch line that took no branches +- Fix: the LCOV report code assumed that a branch line that took no branches meant that the entire line was unexecuted. This isn't true in a few cases: the line might always raise an exception, or might have been optimized away. Fixes `issue 1896`_. +- Fix: similarly, the HTML report will now explain that a line that jumps to + none of its expected destinations must have always raised an exception. + Previously, it would say something nonsensical like, "line 4 didn't jump to + line 5 because line 4 was never true, and it didn't jump to line 7 because + line 4 was always true." This was also shown in `issue 1896`_. + .. _issue 1896: https://github.com/nedbat/coveragepy/issues/1896 diff --git a/coverage/html.py b/coverage/html.py index 20a354b12..2cc68ac1d 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -137,6 +137,7 @@ def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData: contexts_by_lineno = self.data.contexts_by_lineno(analysis.filename) lines = [] + branch_stats = analysis.branch_stats() for lineno, tokens in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. @@ -150,12 +151,22 @@ def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData: category = "mis" elif self.has_arcs and lineno in missing_branch_arcs: category = "par" - for b in missing_branch_arcs[lineno]: - if b < 0: - short_annotations.append("exit") - else: - short_annotations.append(str(b)) - long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed)) + mba = missing_branch_arcs[lineno] + if len(mba) == branch_stats[lineno][0]: + # None of the branches were taken from this line. + short_annotations.append("anywhere") + long_annotations.append( + f"line {lineno} didn't jump anywhere: it always raised an exception." + ) + else: + for b in missing_branch_arcs[lineno]: + if b < 0: + short_annotations.append("exit") + else: + short_annotations.append(str(b)) + long_annotations.append( + fr.missing_arc_description(lineno, b, arcs_executed) + ) elif lineno in analysis.statements: category = "run" @@ -486,16 +497,12 @@ def write_html_page(self, ftr: FileToReport) -> None: if ldata.long_annotations: longs = ldata.long_annotations - if len(longs) == 1: - ldata.annotate_long = longs[0] - else: - ldata.annotate_long = "{:d} missed branches: {}".format( - len(longs), - ", ".join( - f"{num:d}) {ann_long}" - for num, ann_long in enumerate(longs, start=1) - ), - ) + # A line can only have two branch destinations. If there were + # two missing, we would have written one as "always raised." + assert len(longs) == 1, ( + f"Had long annotations in {ftr.fr.relative_filename()}: {longs}" + ) + ldata.annotate_long = longs[0] else: ldata.annotate_long = None diff --git a/tests/gold/html/b_branch/b_py.html b/tests/gold/html/b_branch/b_py.html index 62a849f1b..ff8938e6c 100644 --- a/tests/gold/html/b_branch/b_py.html +++ b/tests/gold/html/b_branch/b_py.html @@ -66,8 +66,8 @@