@@ -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,69 @@ 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+ const auto NewStartLoc = Skipped->getBegin ();
1318+ const auto EndLoc = Skipped->getEnd ();
1319+
1320+ if (NewStartLoc == EndLoc)
1321+ return ;
1322+ assert (SpellingRegion (SM, NewStartLoc, EndLoc).isInSourceOrder ());
1323+ handleFileExit (NewStartLoc);
1324+ size_t Index = pushRegion ({}, NewStartLoc, EndLoc);
1325+ getRegion ().setSkipped (true );
1326+ handleFileExit (EndLoc);
1327+ popRegions (Index);
1328+ }
1329+
12541330 // / Keep counts of breaks and continues inside loops.
12551331 struct BreakContinue {
12561332 Counter BreakCount;
@@ -1700,43 +1776,119 @@ struct CounterCoverageMappingBuilder
17001776 Visit (S->getSubStmt ());
17011777 }
17021778
1779+ void coverIfConsteval (const IfStmt *S) {
1780+ assert (S->isConsteval ());
1781+
1782+ const auto *Then = S->getThen ();
1783+ const auto *Else = S->getElse ();
1784+
1785+ // It's better for llvm-cov to create a new region with same counter
1786+ // so line-coverage can be properly calculated for lines containing
1787+ // a skipped region (without it the line is marked uncovered)
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 = cast<ConstantExpr>(S->getCond ());
1816+ const bool isTrue = E->getResultAsAPSInt ().getExtValue ();
1817+
1818+ extendRegion (S);
1819+
1820+ // I'm using 'propagateCounts' later as new region is better and allows me
1821+ // to properly calculate line coverage in llvm-cov utility
1822+ const Counter ParentCount = getRegion ().getCounter ();
1823+
1824+ // ignore 'if constexpr ('
1825+ SourceLocation startOfSkipped = S->getIfLoc ();
1826+
1827+ if (const auto *Init = S->getInit ()) {
1828+ const auto start = getStart (Init);
1829+ const auto end = getEnd (Init);
1830+
1831+ // this check is to make sure typedef here which doesn't have valid source
1832+ // location won't crash it
1833+ if (start.isValid () && end.isValid ()) {
1834+ markSkipped (startOfSkipped, start);
1835+ propagateCounts (ParentCount, Init);
1836+ startOfSkipped = getEnd (Init);
1837+ }
1838+ }
1839+
1840+ const auto *Then = S->getThen ();
1841+ const auto *Else = S->getElse ();
1842+
1843+ if (isTrue) {
1844+ // ignore '<condition>)'
1845+ markSkipped (startOfSkipped, getStart (Then));
1846+ propagateCounts (ParentCount, Then);
1847+
1848+ if (Else)
1849+ // ignore 'else <else>'
1850+ markSkipped (getEnd (Then), getEnd (Else));
1851+ } else {
1852+ // ignore '<condition>) <then> [else]'
1853+ markSkipped (startOfSkipped, Else ? getStart (Else) : getEnd (Then));
1854+
1855+ if (Else)
1856+ propagateCounts (ParentCount, Else);
1857+ }
1858+ }
1859+
17031860 void VisitIfStmt (const IfStmt *S) {
1861+ // "if constexpr" and "if consteval" are not normal conditional statements,
1862+ // their discarded statement should be skipped
1863+ if (S->isConsteval ())
1864+ return coverIfConsteval (S);
1865+ else if (S->isConstexpr ())
1866+ return coverIfConstexpr (S);
1867+
17041868 extendRegion (S);
17051869 if (S->getInit ())
17061870 Visit (S->getInit ());
17071871
17081872 // Extend into the condition before we propagate through it below - this is
17091873 // needed to handle macros that generate the "if" but not the condition.
1710- if (!S->isConsteval ())
1711- extendRegion (S->getCond ());
1874+ extendRegion (S->getCond ());
17121875
17131876 Counter ParentCount = getRegion ().getCounter ();
1877+ Counter ThenCount = getRegionCounter (S);
17141878
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);
1879+ // Emitting a counter for the condition makes it easier to interpret the
1880+ // counter for the body when looking at the coverage.
1881+ propagateCounts (ParentCount, S->getCond ());
17191882
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- }
1883+ // The 'then' count applies to the area immediately after the condition.
1884+ std::optional<SourceRange> Gap =
1885+ findGapAreaBetween (S->getRParenLoc (), getStart (S->getThen ()));
1886+ if (Gap)
1887+ fillGapAreaWithCount (Gap->getBegin (), Gap->getEnd (), ThenCount);
17311888
17321889 extendRegion (S->getThen ());
17331890 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);
1891+ Counter ElseCount = subtractCounters (ParentCount, ThenCount);
17401892
17411893 if (const Stmt *Else = S->getElse ()) {
17421894 bool ThenHasTerminateStmt = HasTerminateStmt;
@@ -1759,11 +1911,9 @@ struct CounterCoverageMappingBuilder
17591911 GapRegionCounter = OutCount;
17601912 }
17611913
1762- if (!S->isConsteval ()) {
1763- // Create Branch Region around condition.
1764- createBranchRegion (S->getCond (), ThenCount,
1765- subtractCounters (ParentCount, ThenCount));
1766- }
1914+ // Create Branch Region around condition.
1915+ createBranchRegion (S->getCond (), ThenCount,
1916+ subtractCounters (ParentCount, ThenCount));
17671917 }
17681918
17691919 void VisitCXXTryStmt (const CXXTryStmt *S) {
0 commit comments