Skip to content

Commit d1a916a

Browse files
committed
fix: a line that branches nowhere must always raise an exception
1 parent 2ace7a2 commit d1a916a

File tree

4 files changed

+38
-30
lines changed

4 files changed

+38
-30
lines changed

CHANGES.rst

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@ upgrading your version of coverage.py.
2323
Unreleased
2424
----------
2525

26-
- fix: the LCOV report code assumed that a branch line that took no branches
26+
- Fix: the LCOV report code assumed that a branch line that took no branches
2727
meant that the entire line was unexecuted. This isn't true in a few cases:
2828
the line might always raise an exception, or might have been optimized away.
2929
Fixes `issue 1896`_.
3030

31+
- Fix: similarly, the HTML report will now explain that a line that jumps to
32+
none of its expected destinations must have always raised an exception.
33+
Previously, it would say something nonsensical like, "line 4 didn't jump to
34+
line 5 because line 4 was never true, and it didn't jump to line 7 because
35+
line 4 was always true." This was also shown in `issue 1896`_.
36+
3137
.. _issue 1896: https://github.com/nedbat/coveragepy/issues/1896
3238

3339

coverage/html.py

+23-16
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData:
137137
contexts_by_lineno = self.data.contexts_by_lineno(analysis.filename)
138138

139139
lines = []
140+
branch_stats = analysis.branch_stats()
140141

141142
for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
142143
# Figure out how to mark this line.
@@ -150,12 +151,22 @@ def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData:
150151
category = "mis"
151152
elif self.has_arcs and lineno in missing_branch_arcs:
152153
category = "par"
153-
for b in missing_branch_arcs[lineno]:
154-
if b < 0:
155-
short_annotations.append("exit")
156-
else:
157-
short_annotations.append(str(b))
158-
long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed))
154+
mba = missing_branch_arcs[lineno]
155+
if len(mba) == branch_stats[lineno][0]:
156+
# None of the branches were taken from this line.
157+
short_annotations.append("anywhere")
158+
long_annotations.append(
159+
f"line {lineno} didn't jump anywhere: it always raised an exception."
160+
)
161+
else:
162+
for b in missing_branch_arcs[lineno]:
163+
if b < 0:
164+
short_annotations.append("exit")
165+
else:
166+
short_annotations.append(str(b))
167+
long_annotations.append(
168+
fr.missing_arc_description(lineno, b, arcs_executed)
169+
)
159170
elif lineno in analysis.statements:
160171
category = "run"
161172

@@ -486,16 +497,12 @@ def write_html_page(self, ftr: FileToReport) -> None:
486497

487498
if ldata.long_annotations:
488499
longs = ldata.long_annotations
489-
if len(longs) == 1:
490-
ldata.annotate_long = longs[0]
491-
else:
492-
ldata.annotate_long = "{:d} missed branches: {}".format(
493-
len(longs),
494-
", ".join(
495-
f"{num:d}) {ann_long}"
496-
for num, ann_long in enumerate(longs, start=1)
497-
),
498-
)
500+
# A line can only have two branch destinations. If there were
501+
# two missing, we would have written one as "always raised."
502+
assert len(longs) == 1, (
503+
f"Had long annotations in {ftr.fr.relative_filename()}: {longs}"
504+
)
505+
ldata.annotate_long = longs[0]
499506
else:
500507
ldata.annotate_long = None
501508

tests/gold/html/b_branch/b_py.html

+5-5
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ <h2>
6666
<a id="indexLink" class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
6767
<a id="nextFileLink" class="nav" href="index.html">&#xbb; next</a>
6868
&nbsp; &nbsp; &nbsp;
69-
<a class="nav" href="https://coverage.readthedocs.io/en/7.6.0a0.dev1">coverage.py v7.6.0a0.dev1</a>,
70-
created at 2024-07-10 12:20 -0400
69+
<a class="nav" href="https://coverage.readthedocs.io/en/7.6.8a0.dev1">coverage.py v7.6.8a0.dev1</a>,
70+
created at 2024-11-23 15:15 -0500
7171
</p>
7272
<aside class="hidden">
7373
<button type="button" class="button_next_chunk" data-shortcut="j"></button>
@@ -101,7 +101,7 @@ <h2>
101101
<p class="run"><span class="n"><a id="t17" href="#t17">17</a></span><span class="t"><span class="key">def</span> <span class="nam">three</span><span class="op">(</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
102102
<p class="run"><span class="n"><a id="t18" href="#t18">18</a></span><span class="t"> <span class="key">try</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
103103
<p class="pln"><span class="n"><a id="t19" href="#t19">19</a></span><span class="t"> <span class="com"># This if has two branches, *neither* one taken.</span>&nbsp;</span><span class="r"></span></p>
104-
<p class="par run show_par"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"> <span class="key">if</span> <span class="nam">name_error_this_variable_doesnt_exist</span><span class="op">:</span>&nbsp;</span><span class="r"><span class="annotate short">20&#x202F;&#x219B;&#x202F;21,&nbsp;&nbsp; 20&#x202F;&#x219B;&#x202F;23</span><span class="annotate long">2 missed branches: 1) line 20 didn't jump to line 21 because the condition on line 20 was never true, 2) line 20 didn't jump to line 23 because the condition on line 20 was always true</span></span></p>
104+
<p class="par run show_par"><span class="n"><a id="t20" href="#t20">20</a></span><span class="t"> <span class="key">if</span> <span class="nam">name_error_this_variable_doesnt_exist</span><span class="op">:</span>&nbsp;</span><span class="r"><span class="annotate short">20&#x202F;&#x219B;&#x202F;anywhere</span><span class="annotate long">line 20 didn't jump anywhere: it always raised an exception.</span></span></p>
105105
<p class="mis show_mis"><span class="n"><a id="t21" href="#t21">21</a></span><span class="t"> <span class="nam">a</span> <span class="op">=</span> <span class="num">1</span>&nbsp;</span><span class="r"></span></p>
106106
<p class="pln"><span class="n"><a id="t22" href="#t22">22</a></span><span class="t"> <span class="key">else</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
107107
<p class="mis show_mis"><span class="n"><a id="t23" href="#t23">23</a></span><span class="t"> <span class="nam">a</span> <span class="op">=</span> <span class="num">2</span>&nbsp;</span><span class="r"></span></p>
@@ -117,8 +117,8 @@ <h2>
117117
<a class="nav" href="index.html">&Hat; index</a> &nbsp; &nbsp;
118118
<a class="nav" href="index.html">&#xbb; next</a>
119119
&nbsp; &nbsp; &nbsp;
120-
<a class="nav" href="https://coverage.readthedocs.io/en/7.6.0a0.dev1">coverage.py v7.6.0a0.dev1</a>,
121-
created at 2024-07-10 12:20 -0400
120+
<a class="nav" href="https://coverage.readthedocs.io/en/7.6.8a0.dev1">coverage.py v7.6.8a0.dev1</a>,
121+
created at 2024-11-23 15:15 -0500
122122
</p>
123123
</div>
124124
</footer>

tests/test_html.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,6 @@ def three():
840840
cov = coverage.Coverage(branch=True)
841841
b = self.start_import_stop(cov, "b")
842842
cov.html_report(b, directory="out/b_branch")
843-
844843
compare_html(gold_path("html/b_branch"), "out/b_branch")
845844
contains(
846845
"out/b_branch/b_py.html",
@@ -856,13 +855,9 @@ def three():
856855
('<span class="annotate short">12&#x202F;&#x219B;&#x202F;exit</span>' +
857856
'<span class="annotate long">line 12 didn\'t return from function \'two\' ' +
858857
'because the condition on line 12 was always true</span>'),
859-
('<span class="annotate short">20&#x202F;&#x219B;&#x202F;21,&nbsp;&nbsp; ' +
860-
'20&#x202F;&#x219B;&#x202F;23</span>' +
861-
'<span class="annotate long">2 missed branches: ' +
862-
'1) line 20 didn\'t jump to line 21 ' +
863-
'because the condition on line 20 was never true, ' +
864-
'2) line 20 didn\'t jump to line 23 ' +
865-
'because the condition on line 20 was always true</span>'),
858+
('<span class="annotate short">20&#x202F;&#x219B;&#x202F;anywhere</span>' +
859+
'<span class="annotate long">line 20 didn\'t jump anywhere: ' +
860+
'it always raised an exception.</span>'),
866861
)
867862
contains(
868863
"out/b_branch/index.html",

0 commit comments

Comments
 (0)