Skip to content

Commit f4cf610

Browse files
authored
[Coverage] Add gap region between binary operator '&& and ||' and RHS (#149085)
## Issue Summary We identified an inaccuracy in line coverage reporting when short-circuit evaluation occurs in multi-line conditional expressions. Specifically: 1. Un-executed conditions following line breaks may be incorrectly marked as covered (e.g., conditionB in a non-executed && chain shows coverage) ``` 1| |#include <iostream> 2| | 3| 1|int main() { 4| 1| bool conditionA = false; 5| 1| bool conditionB = true; 6| 1| if (conditionA && 7| 1| conditionB) { 8| 0| std::cout << "IF-THEN" << std::endl; 9| 0| } 10| 1| return 0; 11| 1|} ``` 2. Inconsistent coverage reporting across un-executed conditions *(adjacent un-executed conditions may show 1 vs 0 line coverage)* ``` 1| |#include <iostream> 2| | 3| 1|int main() { 4| 1| bool conditionA = false; 5| 1| bool conditionB = true; 6| 1| bool conditionC = true; 7| 1| if (conditionA && 8| 1| (conditionB || 9| 0| conditionC)) { 10| 0| std::cout << "IF-THEN" << std::endl; 11| 0| } 12| 1| return 0; 13| 1|} ``` This is resolved by inserting a GapRegion when mapping logical operators to isolate coverage contexts around short-circuit evaluation.
1 parent 0fffb9f commit f4cf610

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

clang/lib/CodeGen/CoverageMappingGen.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,11 @@ struct CounterCoverageMappingBuilder
22692269
// Track LHS True/False Decision.
22702270
const auto DecisionLHS = MCDCBuilder.pop();
22712271

2272+
if (auto Gap =
2273+
findGapAreaBetween(getEnd(E->getLHS()), getStart(E->getRHS()))) {
2274+
fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), getRegionCounter(E));
2275+
}
2276+
22722277
// Counter tracks the right hand side of a logical and operator.
22732278
extendRegion(E->getRHS());
22742279
propagateCounts(getRegionCounter(E), E->getRHS());
@@ -2330,6 +2335,11 @@ struct CounterCoverageMappingBuilder
23302335
// Track LHS True/False Decision.
23312336
const auto DecisionLHS = MCDCBuilder.pop();
23322337

2338+
if (auto Gap =
2339+
findGapAreaBetween(getEnd(E->getLHS()), getStart(E->getRHS()))) {
2340+
fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), getRegionCounter(E));
2341+
}
2342+
23332343
// Counter tracks the right hand side of a logical or operator.
23342344
extendRegion(E->getRHS());
23352345
propagateCounts(getRegionCounter(E), E->getRHS());
Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name logical.cpp %s | FileCheck %s
22
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name logical.cpp %s | FileCheck %s -check-prefix=MCDC
33

4-
int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+23]]:2 = #0
4+
int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+27]]:2 = #0
55
bool bt = true;
66
bool bf = false; // MCDC: Decision,File 0, [[@LINE+1]]:12 -> [[@LINE+1]]:20 = M:3, C:2
77
bool a = bt && bf; // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE]]:14 = #0
88
// CHECK-NEXT: Branch,File 0, [[@LINE-1]]:12 -> [[@LINE-1]]:14 = #1, (#0 - #1)
9-
// CHECK-NEXT: File 0, [[@LINE-2]]:18 -> [[@LINE-2]]:20 = #1
10-
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:18 -> [[@LINE-3]]:20 = #2, (#1 - #2)
9+
// CHECK-NEXT: Gap,File 0, [[@LINE-2]]:14 -> [[@LINE-2]]:18 = #1
10+
// CHECK-NEXT: File 0, [[@LINE-3]]:18 -> [[@LINE-3]]:20 = #1
11+
// CHECK-NEXT: Branch,File 0, [[@LINE-4]]:18 -> [[@LINE-4]]:20 = #2, (#1 - #2)
1112
// MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+2]]:9 = M:6, C:2
1213
a = bt && // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
1314
bf; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3, (#0 - #3)
14-
// CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3
15-
// CHECK-NEXT: Branch,File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = #4, (#3 - #4)
15+
// CHECK-NEXT: Gap,File 0, [[@LINE-2]]:9 -> [[@LINE-1]]:7 = #3
16+
// CHECK-NEXT: File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = #3
17+
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:7 -> [[@LINE-3]]:9 = #4, (#3 - #4)
1618
// MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:15 = M:9, C:2
1719
a = bf || bt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
1820
// CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #5), #5
19-
// CHECK-NEXT: File 0, [[@LINE-2]]:13 -> [[@LINE-2]]:15 = #5
20-
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:13 -> [[@LINE-3]]:15 = (#5 - #6), #6
21+
// CHECK-NEXT: Gap,File 0, [[@LINE-2]]:9 -> [[@LINE-2]]:13 = #5
22+
// CHECK-NEXT: File 0, [[@LINE-3]]:13 -> [[@LINE-3]]:15 = #5
23+
// CHECK-NEXT: Branch,File 0, [[@LINE-4]]:13 -> [[@LINE-4]]:15 = (#5 - #6), #6
2124
// MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+2]]:9 = M:12, C:2
2225
a = bf || // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
2326
bt; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #7), #7
24-
// CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #7
25-
// CHECK-NEXT: Branch,File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = (#7 - #8), #8
27+
// CHECK-NEXT: Gap,File 0, [[@LINE-2]]:9 -> [[@LINE-1]]:7 = #7
28+
// CHECK-NEXT: File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = #7
29+
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:7 -> [[@LINE-3]]:9 = (#7 - #8), #8
2630
return 0;
2731
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clangxx_profgen -std=c++17 -fuse-ld=lld -fcoverage-mapping -o %t %s
2+
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
3+
// RUN: llvm-profdata merge -o %t.profdata %t.profraw
4+
// RUN: llvm-cov show %t -instr-profile=%t.profdata 2>&1 | FileCheck %s
5+
6+
void foo() { // CHECK: [[@LINE]]| 1|void foo() {
7+
bool cond1 = false; // CHECK-NEXT: [[@LINE]]| 1| bool cond1 = false;
8+
bool cond2 = true; // CHECK-NEXT: [[@LINE]]| 1| bool cond2 = true;
9+
if (cond1 && // CHECK-NEXT: [[@LINE]]| 1| if (cond1 &&
10+
cond2) { // CHECK-NEXT: [[@LINE]]| 0| cond2) {
11+
} // CHECK-NEXT: [[@LINE]]| 0| }
12+
} // CHECK-NEXT: [[@LINE]]| 1|}
13+
14+
void bar() { // CHECK: [[@LINE]]| 1|void bar() {
15+
bool cond1 = true; // CHECK-NEXT: [[@LINE]]| 1| bool cond1 = true;
16+
bool cond2 = false; // CHECK-NEXT: [[@LINE]]| 1| bool cond2 = false;
17+
if (cond1 && // CHECK-NEXT: [[@LINE]]| 1| if (cond1 &&
18+
cond2) { // CHECK-NEXT: [[@LINE]]| 1| cond2) {
19+
} // CHECK-NEXT: [[@LINE]]| 0| }
20+
} // CHECK-NEXT: [[@LINE]]| 1|}
21+
22+
void baz() { // CHECK: [[@LINE]]| 1|void baz() {
23+
bool cond1 = false; // CHECK-NEXT: [[@LINE]]| 1| bool cond1 = false;
24+
bool cond2 = true; // CHECK-NEXT: [[@LINE]]| 1| bool cond2 = true;
25+
if (cond1 // CHECK-NEXT: [[@LINE]]| 1| if (cond1
26+
&& // CHECK-NEXT: [[@LINE]]| 0| &&
27+
cond2) { // CHECK-NEXT: [[@LINE]]| 0| cond2) {
28+
} // CHECK-NEXT: [[@LINE]]| 0| }
29+
} // CHECK-NEXT: [[@LINE]]| 1|}
30+
31+
int main() {
32+
foo();
33+
bar();
34+
baz();
35+
return 0;
36+
}

0 commit comments

Comments
 (0)