@@ -119,26 +119,31 @@ class SourceMappingRegion {
119
119
// / as the line execution count if there are no other regions on the line.
120
120
bool GapRegion;
121
121
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
+
122
126
public:
123
127
SourceMappingRegion (Counter Count, std::optional<SourceLocation> LocStart,
124
128
std::optional<SourceLocation> LocEnd,
125
129
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 ) { }
128
132
129
133
SourceMappingRegion (Counter Count, std::optional<Counter> FalseCount,
130
134
MCDCParameters MCDCParams,
131
135
std::optional<SourceLocation> LocStart,
132
136
std::optional<SourceLocation> LocEnd,
133
137
bool GapRegion = false )
134
138
: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
135
- LocStart (LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {}
139
+ LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
140
+ SkippedRegion(false ) {}
136
141
137
142
SourceMappingRegion (MCDCParameters MCDCParams,
138
143
std::optional<SourceLocation> LocStart,
139
144
std::optional<SourceLocation> LocEnd)
140
145
: MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
141
- GapRegion(false ) {}
146
+ GapRegion(false ), SkippedRegion( false ) {}
142
147
143
148
const Counter &getCounter () const { return Count; }
144
149
@@ -174,6 +179,10 @@ class SourceMappingRegion {
174
179
175
180
void setGap (bool Gap) { GapRegion = Gap; }
176
181
182
+ bool isSkipped () const { return SkippedRegion; }
183
+
184
+ void setSkipped (bool Skipped) { SkippedRegion = Skipped; }
185
+
177
186
bool isBranch () const { return FalseCount.has_value (); }
178
187
179
188
bool isMCDCDecision () const { return MCDCParams.NumConditions != 0 ; }
@@ -468,6 +477,10 @@ class CoverageMappingBuilder {
468
477
MappingRegions.push_back (CounterMappingRegion::makeGapRegion (
469
478
Region.getCounter (), *CovFileID, SR.LineStart , SR.ColumnStart ,
470
479
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 ));
471
484
} else if (Region.isBranch ()) {
472
485
MappingRegions.push_back (CounterMappingRegion::makeBranchRegion (
473
486
Region.getCounter (), Region.getFalseCounter (),
@@ -1251,6 +1264,70 @@ struct CounterCoverageMappingBuilder
1251
1264
popRegions (Index);
1252
1265
}
1253
1266
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
+
1254
1331
// / Keep counts of breaks and continues inside loops.
1255
1332
struct BreakContinue {
1256
1333
Counter BreakCount;
@@ -1700,43 +1777,116 @@ struct CounterCoverageMappingBuilder
1700
1777
Visit (S->getSubStmt ());
1701
1778
}
1702
1779
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
+
1703
1858
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
+
1704
1866
extendRegion (S);
1705
1867
if (S->getInit ())
1706
1868
Visit (S->getInit ());
1707
1869
1708
1870
// Extend into the condition before we propagate through it below - this is
1709
1871
// needed to handle macros that generate the "if" but not the condition.
1710
- if (!S->isConsteval ())
1711
- extendRegion (S->getCond ());
1872
+ extendRegion (S->getCond ());
1712
1873
1713
1874
Counter ParentCount = getRegion ().getCounter ();
1875
+ Counter ThenCount = getRegionCounter (S);
1714
1876
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 ());
1719
1880
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);
1731
1886
1732
1887
extendRegion (S->getThen ());
1733
1888
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);
1740
1890
1741
1891
if (const Stmt *Else = S->getElse ()) {
1742
1892
bool ThenHasTerminateStmt = HasTerminateStmt;
@@ -1759,11 +1909,9 @@ struct CounterCoverageMappingBuilder
1759
1909
GapRegionCounter = OutCount;
1760
1910
}
1761
1911
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));
1767
1915
}
1768
1916
1769
1917
void VisitCXXTryStmt (const CXXTryStmt *S) {
0 commit comments