void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS, const LineCoverageStats &Line, unsigned ViewDepth) { renderLinePrefix(OS, ViewDepth); OS.indent(getCombinedColumnWidth(getOptions())); CoverageSegmentArray Segments = Line.getLineSegments(); // Just consider the segments which start *and* end on this line. if (Segments.size() > 1) Segments = Segments.drop_back(); unsigned PrevColumn = 1; for (const auto *S : Segments) { if (!S->IsRegionEntry) continue; if (S->Count == Line.getExecutionCount()) continue; // Skip to the new region. if (S->Col > PrevColumn) OS.indent(S->Col - PrevColumn); PrevColumn = S->Col + 1; std::string C = formatCount(S->Count); PrevColumn += C.size(); OS << '^' << C; if (getOptions().Debug) errs() << "Marker at " << S->Line << ":" << S->Col << " = " << formatCount(S->Count) << "\n"; } OS << '\n'; }
void SourceCoverageViewHTML::renderLineCoverageColumn( raw_ostream &OS, const LineCoverageStats &Line) { std::string Count = ""; if (Line.isMapped()) Count = tag("pre", formatCount(Line.getExecutionCount())); std::string CoverageClass = (Line.getExecutionCount() > 0) ? "covered-line" : "uncovered-line"; OS << tag("td", Count, CoverageClass); }
void SourceCoverageViewText::renderLineCoverageColumn( raw_ostream &OS, const LineCoverageStats &Line) { if (!Line.isMapped()) { OS.indent(LineCoverageColumnWidth) << '|'; return; } std::string C = formatCount(Line.ExecutionCount); OS.indent(LineCoverageColumnWidth - C.size()); colored_ostream(OS, raw_ostream::MAGENTA, Line.hasMultipleRegions() && getOptions().Colors) << C; OS << '|'; }
void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS, unsigned ExpansionCol, unsigned ViewDepth) { StringRef Line = L.Line; unsigned LineNumber = L.LineNo; auto *WrappedSegment = LCS.getWrappedSegment(); CoverageSegmentArray Segments = LCS.getLineSegments(); Optional<raw_ostream::Colors> Highlight; SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; // The first segment overlaps from a previous line, so we treat it specially. if (WrappedSegment && !WrappedSegment->IsGapRegion && WrappedSegment->HasCount && WrappedSegment->Count == 0) Highlight = raw_ostream::RED; // Output each segment of the line, possibly highlighted. unsigned Col = 1; for (const auto *S : Segments) { unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1); colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true) << Line.substr(Col - 1, End - Col); if (getOptions().Debug && Highlight) HighlightedRanges.push_back(std::make_pair(Col, End)); Col = End; if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) && S->HasCount && S->Count == 0) Highlight = raw_ostream::RED; else if (Col == ExpansionCol) Highlight = raw_ostream::CYAN; else Highlight = None; } // Show the rest of the line. colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true) << Line.substr(Col - 1, Line.size() - Col + 1); OS << '\n'; if (getOptions().Debug) { for (const auto &Range : HighlightedRanges) errs() << "Highlighted line " << LineNumber << ", " << Range.first << " -> " << Range.second << '\n'; if (Highlight) errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; } }
bool SourceCoverageView::shouldRenderRegionMarkers( const LineCoverageStats &LCS) const { if (!getOptions().ShowRegionMarkers) return false; CoverageSegmentArray Segments = LCS.getLineSegments(); if (Segments.empty()) return false; for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { const auto *CurSeg = Segments[I]; if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount()) continue; return true; } return false; }
void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, unsigned ViewDepth) { if (ShowSourceName) renderSourceName(OS); renderViewHeader(OS); // We need the expansions and instantiations sorted so we can go through them // while we iterate lines. std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); auto NextESV = ExpansionSubViews.begin(); auto EndESV = ExpansionSubViews.end(); auto NextISV = InstantiationSubViews.begin(); auto EndISV = InstantiationSubViews.end(); // Get the coverage information for the file. auto NextSegment = CoverageInfo.begin(); auto EndSegment = CoverageInfo.end(); unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0; const coverage::CoverageSegment *WrappedSegment = nullptr; SmallVector<const coverage::CoverageSegment *, 8> LineSegments; for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { // If we aren't rendering the whole file, we need to filter out the prologue // and epilogue. if (!WholeFile) { if (NextSegment == EndSegment) break; else if (LI.line_number() < FirstLine) continue; } // Collect the coverage information relevant to this line. if (LineSegments.size()) WrappedSegment = LineSegments.back(); LineSegments.clear(); while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) LineSegments.push_back(&*NextSegment++); // Calculate a count to be for the line as a whole. LineCoverageStats LineCount; if (WrappedSegment && WrappedSegment->HasCount) LineCount.addRegionCount(WrappedSegment->Count); for (const auto *S : LineSegments) if (S->HasCount && S->IsRegionEntry) LineCount.addRegionStartCount(S->Count); renderLinePrefix(OS, ViewDepth); if (getOptions().ShowLineStats) renderLineCoverageColumn(OS, LineCount); if (getOptions().ShowLineNumbers) renderLineNumberColumn(OS, LI.line_number()); // If there are expansion subviews, we want to highlight the first one. unsigned ExpansionColumn = 0; if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && getOptions().Colors) ExpansionColumn = NextESV->getStartCol(); // Display the source code for the current line. renderLine(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments, ExpansionColumn, ViewDepth); // Show the region markers. if (shouldRenderRegionMarkers(LineCount.hasMultipleRegions())) renderRegionMarkers(OS, LineSegments, ViewDepth); // Show the expansions and instantiations for this line. bool RenderedSubView = false; for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); ++NextESV) { renderViewDivider(OS, ViewDepth + 1); // Re-render the current line and highlight the expansion range for // this subview. if (RenderedSubView) { ExpansionColumn = NextESV->getStartCol(); renderExpansionSite(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments, ExpansionColumn, ViewDepth); renderViewDivider(OS, ViewDepth + 1); } renderExpansionView(OS, *NextESV, ViewDepth + 1); RenderedSubView = true; } for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { renderViewDivider(OS, ViewDepth + 1); renderInstantiationView(OS, *NextISV, ViewDepth + 1); RenderedSubView = true; } if (RenderedSubView) renderViewDivider(OS, ViewDepth + 1); renderLineSuffix(OS, ViewDepth); } renderViewFooter(OS); }
void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS, unsigned ExpansionCol, unsigned) { StringRef Line = L.Line; unsigned LineNo = L.LineNo; // Steps for handling text-escaping, highlighting, and tooltip creation: // // 1. Split the line into N+1 snippets, where N = |Segments|. The first // snippet starts from Col=1 and ends at the start of the first segment. // The last snippet starts at the last mapped column in the line and ends // at the end of the line. Both are required but may be empty. SmallVector<std::string, 8> Snippets; CoverageSegmentArray Segments = LCS.getLineSegments(); unsigned LCol = 1; auto Snip = [&](unsigned Start, unsigned Len) { Snippets.push_back(Line.substr(Start, Len)); LCol += Len; }; Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1)); for (unsigned I = 1, E = Segments.size(); I < E; ++I) Snip(LCol - 1, Segments[I]->Col - LCol); // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1. Snip(LCol - 1, Line.size() + 1 - LCol); // 2. Escape all of the snippets. for (unsigned I = 0, E = Snippets.size(); I < E; ++I) Snippets[I] = escape(Snippets[I], getOptions()); // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment // 1 to set the highlight for snippet 2, segment 2 to set the highlight for // snippet 3, and so on. Optional<StringRef> Color; SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) { if (getOptions().Debug) HighlightedRanges.emplace_back(LC, RC); return tag("span", Snippet, Color.getValue()); }; auto CheckIfUncovered = [&](const CoverageSegment *S) { return S && (!S->IsGapRegion || (Color && *Color == "red")) && S->HasCount && S->Count == 0; }; if (CheckIfUncovered(LCS.getWrappedSegment())) { Color = "red"; if (!Snippets[0].empty()) Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size()); } for (unsigned I = 0, E = Segments.size(); I < E; ++I) { const auto *CurSeg = Segments[I]; if (CheckIfUncovered(CurSeg)) Color = "red"; else if (CurSeg->Col == ExpansionCol) Color = "cyan"; else Color = None; if (Color.hasValue()) Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col, CurSeg->Col + Snippets[I + 1].size()); } if (Color.hasValue() && Segments.empty()) Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size()); if (getOptions().Debug) { for (const auto &Range : HighlightedRanges) { errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> "; if (Range.second == 0) errs() << "?"; else errs() << Range.second; errs() << "\n"; } } // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate // sub-line region count tooltips if needed. if (shouldRenderRegionMarkers(LCS)) { // Just consider the segments which start *and* end on this line. for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { const auto *CurSeg = Segments[I]; if (!CurSeg->IsRegionEntry) continue; if (CurSeg->Count == LCS.getExecutionCount()) continue; Snippets[I + 1] = tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count), "tooltip-content"), "tooltip"); if (getOptions().Debug) errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = " << formatCount(CurSeg->Count) << "\n"; } } OS << BeginCodeTD; OS << BeginPre; for (const auto &Snippet : Snippets) OS << Snippet; OS << EndPre; // If there are no sub-views left to attach to this cell, end the cell. // Otherwise, end it after the sub-views are rendered (renderLineSuffix()). if (!hasSubViews()) OS << EndCodeTD; }