@@ -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+
122126public:
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) {
0 commit comments