Skip to content

Commit ae319fd

Browse files
committed
[coverage] skipping code coverage for 'if constexpr' and 'if consteval'
1 parent 30d6806 commit ae319fd

File tree

5 files changed

+317
-79
lines changed

5 files changed

+317
-79
lines changed

clang/lib/CodeGen/CoverageMappingGen.cpp

+180-32
Original file line numberDiff line numberDiff line change
@@ -119,26 +119,31 @@ class SourceMappingRegion {
119119
/// as the line execution count if there are no other regions on the line.
120120
bool GapRegion;
121121

122+
/// Whetever this region is skipped ('if constexpr' or 'if consteval' untaken
123+
/// branch, or anything skipped but not empty line / comments)
124+
bool SkippedRegion;
125+
122126
public:
123127
SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,
124128
std::optional<SourceLocation> LocEnd,
125129
bool GapRegion = false)
126-
: Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {
127-
}
130+
: Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
131+
SkippedRegion(false) {}
128132

129133
SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,
130134
MCDCParameters MCDCParams,
131135
std::optional<SourceLocation> LocStart,
132136
std::optional<SourceLocation> LocEnd,
133137
bool GapRegion = false)
134138
: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
135-
LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {}
139+
LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
140+
SkippedRegion(false) {}
136141

137142
SourceMappingRegion(MCDCParameters MCDCParams,
138143
std::optional<SourceLocation> LocStart,
139144
std::optional<SourceLocation> LocEnd)
140145
: MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
141-
GapRegion(false) {}
146+
GapRegion(false), SkippedRegion(false) {}
142147

143148
const Counter &getCounter() const { return Count; }
144149

@@ -174,6 +179,10 @@ class SourceMappingRegion {
174179

175180
void setGap(bool Gap) { GapRegion = Gap; }
176181

182+
bool isSkipped() const { return SkippedRegion; }
183+
184+
void setSkipped(bool Skipped) { SkippedRegion = Skipped; }
185+
177186
bool isBranch() const { return FalseCount.has_value(); }
178187

179188
bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; }
@@ -468,6 +477,10 @@ class CoverageMappingBuilder {
468477
MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
469478
Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
470479
SR.LineEnd, SR.ColumnEnd));
480+
} else if (Region.isSkipped()) {
481+
MappingRegions.push_back(CounterMappingRegion::makeSkipped(
482+
*CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,
483+
SR.ColumnEnd));
471484
} else if (Region.isBranch()) {
472485
MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
473486
Region.getCounter(), Region.getFalseCounter(),
@@ -1251,6 +1264,70 @@ struct CounterCoverageMappingBuilder
12511264
popRegions(Index);
12521265
}
12531266

1267+
/// Find a valid range starting with \p StartingLoc and ending before \p
1268+
/// BeforeLoc.
1269+
std::optional<SourceRange> findAreaStartingFromTo(SourceLocation StartingLoc,
1270+
SourceLocation BeforeLoc) {
1271+
// If StartingLoc is in function-like macro, use its start location.
1272+
if (StartingLoc.isMacroID()) {
1273+
FileID FID = SM.getFileID(StartingLoc);
1274+
const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion();
1275+
if (EI->isFunctionMacroExpansion())
1276+
StartingLoc = EI->getExpansionLocStart();
1277+
}
1278+
1279+
size_t StartDepth = locationDepth(StartingLoc);
1280+
size_t EndDepth = locationDepth(BeforeLoc);
1281+
while (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc)) {
1282+
bool UnnestStart = StartDepth >= EndDepth;
1283+
bool UnnestEnd = EndDepth >= StartDepth;
1284+
if (UnnestEnd) {
1285+
assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1286+
BeforeLoc));
1287+
1288+
BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1289+
assert(BeforeLoc.isValid());
1290+
EndDepth--;
1291+
}
1292+
if (UnnestStart) {
1293+
assert(SM.isWrittenInSameFile(StartingLoc,
1294+
getStartOfFileOrMacro(StartingLoc)));
1295+
1296+
StartingLoc = getIncludeOrExpansionLoc(StartingLoc);
1297+
assert(StartingLoc.isValid());
1298+
StartDepth--;
1299+
}
1300+
}
1301+
// If the start and end locations of the gap are both within the same macro
1302+
// file, the range may not be in source order.
1303+
if (StartingLoc.isMacroID() || BeforeLoc.isMacroID())
1304+
return std::nullopt;
1305+
if (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc) ||
1306+
!SpellingRegion(SM, StartingLoc, BeforeLoc).isInSourceOrder())
1307+
return std::nullopt;
1308+
return {{StartingLoc, BeforeLoc}};
1309+
}
1310+
1311+
void markSkipped(SourceLocation StartLoc, SourceLocation BeforeLoc) {
1312+
const auto Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc);
1313+
1314+
if (!Skipped) {
1315+
return;
1316+
}
1317+
1318+
const auto NewStartLoc = Skipped->getBegin();
1319+
const auto EndLoc = Skipped->getEnd();
1320+
1321+
if (NewStartLoc == EndLoc)
1322+
return;
1323+
assert(SpellingRegion(SM, NewStartLoc, EndLoc).isInSourceOrder());
1324+
handleFileExit(NewStartLoc);
1325+
size_t Index = pushRegion({}, NewStartLoc, EndLoc);
1326+
getRegion().setSkipped(true);
1327+
handleFileExit(EndLoc);
1328+
popRegions(Index);
1329+
}
1330+
12541331
/// Keep counts of breaks and continues inside loops.
12551332
struct BreakContinue {
12561333
Counter BreakCount;
@@ -1700,43 +1777,116 @@ struct CounterCoverageMappingBuilder
17001777
Visit(S->getSubStmt());
17011778
}
17021779

1780+
void CoverIfConsteval(const IfStmt *S) {
1781+
assert(S->isConsteval());
1782+
1783+
const auto *Then = S->getThen();
1784+
const auto *Else = S->getElse();
1785+
1786+
// I'm using 'propagateCounts' later as new region is better and allows me
1787+
// to properly calculate line coverage in llvm-cov utility
1788+
const Counter ParentCount = getRegion().getCounter();
1789+
1790+
extendRegion(S);
1791+
1792+
if (S->isNegatedConsteval()) {
1793+
// ignore 'if consteval'
1794+
markSkipped(S->getIfLoc(), getStart(Then));
1795+
propagateCounts(ParentCount, Then);
1796+
1797+
if (Else) {
1798+
// ignore 'else <else>'
1799+
markSkipped(getEnd(Then), getEnd(Else));
1800+
}
1801+
} else {
1802+
assert(S->isNonNegatedConsteval());
1803+
// ignore 'if consteval <then> [else]'
1804+
markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then));
1805+
1806+
if (Else)
1807+
propagateCounts(ParentCount, Else);
1808+
}
1809+
}
1810+
1811+
void CoverIfConstexpr(const IfStmt *S) {
1812+
assert(S->isConstexpr());
1813+
1814+
// evaluate constant condition...
1815+
const auto *E = dyn_cast<ConstantExpr>(S->getCond());
1816+
assert(E != nullptr);
1817+
const bool isTrue = E->getResultAsAPSInt().getExtValue();
1818+
1819+
extendRegion(S);
1820+
1821+
const auto *Init = S->getInit();
1822+
const auto *Then = S->getThen();
1823+
const auto *Else = S->getElse();
1824+
1825+
// I'm using 'propagateCounts' later as new region is better and allows me
1826+
// to properly calculate line coverage in llvm-cov utility
1827+
const Counter ParentCount = getRegion().getCounter();
1828+
1829+
// ignore 'if constexpr ('
1830+
SourceLocation startOfSkipped = S->getIfLoc();
1831+
1832+
if (Init) {
1833+
// don't mark initialisation as ignored
1834+
markSkipped(startOfSkipped, getStart(Init));
1835+
propagateCounts(ParentCount, Init);
1836+
// ignore after initialisation: '; <condition>)'...
1837+
startOfSkipped = getEnd(Init);
1838+
}
1839+
1840+
if (isTrue) {
1841+
// ignore '<condition>)'
1842+
markSkipped(startOfSkipped, getStart(Then));
1843+
propagateCounts(ParentCount, Then);
1844+
1845+
if (Else)
1846+
// ignore 'else <else>'
1847+
markSkipped(getEnd(Then), getEnd(Else));
1848+
} else {
1849+
// ignore '<condition>) <then> [else]'
1850+
markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then));
1851+
1852+
if (Else) {
1853+
propagateCounts(ParentCount, Else);
1854+
}
1855+
}
1856+
}
1857+
17031858
void VisitIfStmt(const IfStmt *S) {
1859+
// "if constexpr" and "if consteval" are not normal conditional statements,
1860+
// they should behave more like a preprocessor conditions
1861+
if (S->isConsteval())
1862+
return CoverIfConsteval(S);
1863+
else if (S->isConstexpr())
1864+
return CoverIfConstexpr(S);
1865+
17041866
extendRegion(S);
17051867
if (S->getInit())
17061868
Visit(S->getInit());
17071869

17081870
// Extend into the condition before we propagate through it below - this is
17091871
// needed to handle macros that generate the "if" but not the condition.
1710-
if (!S->isConsteval())
1711-
extendRegion(S->getCond());
1872+
extendRegion(S->getCond());
17121873

17131874
Counter ParentCount = getRegion().getCounter();
1875+
Counter ThenCount = getRegionCounter(S);
17141876

1715-
// If this is "if !consteval" the then-branch will never be taken, we don't
1716-
// need to change counter
1717-
Counter ThenCount =
1718-
S->isNegatedConsteval() ? ParentCount : getRegionCounter(S);
1877+
// Emitting a counter for the condition makes it easier to interpret the
1878+
// counter for the body when looking at the coverage.
1879+
propagateCounts(ParentCount, S->getCond());
17191880

1720-
if (!S->isConsteval()) {
1721-
// Emitting a counter for the condition makes it easier to interpret the
1722-
// counter for the body when looking at the coverage.
1723-
propagateCounts(ParentCount, S->getCond());
1724-
1725-
// The 'then' count applies to the area immediately after the condition.
1726-
std::optional<SourceRange> Gap =
1727-
findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
1728-
if (Gap)
1729-
fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
1730-
}
1881+
// The 'then' count applies to the area immediately after the condition.
1882+
std::optional<SourceRange> Gap =
1883+
findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
1884+
if (Gap)
1885+
fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
17311886

17321887
extendRegion(S->getThen());
17331888
Counter OutCount = propagateCounts(ThenCount, S->getThen());
1734-
1735-
// If this is "if consteval" the else-branch will never be taken, we don't
1736-
// need to change counter
1737-
Counter ElseCount = S->isNonNegatedConsteval()
1738-
? ParentCount
1739-
: subtractCounters(ParentCount, ThenCount);
1889+
Counter ElseCount = subtractCounters(ParentCount, ThenCount);
17401890

17411891
if (const Stmt *Else = S->getElse()) {
17421892
bool ThenHasTerminateStmt = HasTerminateStmt;
@@ -1759,11 +1909,9 @@ struct CounterCoverageMappingBuilder
17591909
GapRegionCounter = OutCount;
17601910
}
17611911

1762-
if (!S->isConsteval()) {
1763-
// Create Branch Region around condition.
1764-
createBranchRegion(S->getCond(), ThenCount,
1765-
subtractCounters(ParentCount, ThenCount));
1766-
}
1912+
// Create Branch Region around condition.
1913+
createBranchRegion(S->getCond(), ThenCount,
1914+
subtractCounters(ParentCount, ThenCount));
17671915
}
17681916

17691917
void VisitCXXTryStmt(const CXXTryStmt *S) {

clang/test/CoverageMapping/branch-constfolded.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ bool for_7(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1
9090
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0
9191

9292
// CHECK-LABEL: _Z5for_8b:
93-
bool for_8(bool a) { // MCDC: Decision,File 0, [[@LINE+3]]:17 -> [[@LINE+3]]:30 = M:0, C:2
94-
// CHECK: Branch,File 0, [[@LINE+2]]:17 -> [[@LINE+2]]:21 = 0, 0
95-
// CHECK: Branch,File 0, [[@LINE+1]]:25 -> [[@LINE+1]]:30 = 0, 0
96-
if constexpr (true && false)
93+
bool for_8(bool a) { // MCDC: Decision,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:20 = M:0, C:2
94+
// CHECK: Branch,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = 0, 0
95+
// CHECK: Branch,File 0, [[@LINE+1]]:15 -> [[@LINE+1]]:20 = 0, 0
96+
if (true && false)
9797
return true;
9898
else
9999
return false;

0 commit comments

Comments
 (0)