1 //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 /// \file This file implements the text-based coverage renderer.
11 //===----------------------------------------------------------------------===//
13 #include "CoverageReport.h"
14 #include "SourceCoverageViewText.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringExtras.h"
21 Expected
<CoveragePrinter::OwnedStream
>
22 CoveragePrinterText::createViewFile(StringRef Path
, bool InToplevel
) {
23 return createOutputStream(Path
, "txt", InToplevel
);
26 void CoveragePrinterText::closeViewFile(OwnedStream OS
) {
30 Error
CoveragePrinterText::createIndexFile(
31 ArrayRef
<std::string
> SourceFiles
, const CoverageMapping
&Coverage
,
32 const CoverageFiltersMatchAll
&Filters
) {
33 auto OSOrErr
= createOutputStream("index", "txt", /*InToplevel=*/true);
34 if (Error E
= OSOrErr
.takeError())
36 auto OS
= std::move(OSOrErr
.get());
37 raw_ostream
&OSRef
= *OS
.get();
39 CoverageReport
Report(Opts
, Coverage
);
40 Report
.renderFileReports(OSRef
, SourceFiles
, Filters
);
42 Opts
.colored_ostream(OSRef
, raw_ostream::CYAN
) << "\n"
43 << Opts
.getLLVMVersionString();
45 return Error::success();
50 static const unsigned LineCoverageColumnWidth
= 7;
51 static const unsigned LineNumberColumnWidth
= 5;
53 /// Get the width of the leading columns.
54 unsigned getCombinedColumnWidth(const CoverageViewOptions
&Opts
) {
55 return (Opts
.ShowLineStats
? LineCoverageColumnWidth
+ 1 : 0) +
56 (Opts
.ShowLineNumbers
? LineNumberColumnWidth
+ 1 : 0);
59 /// The width of the line that is used to divide between the view and
61 unsigned getDividerWidth(const CoverageViewOptions
&Opts
) {
62 return getCombinedColumnWidth(Opts
) + 4;
65 } // anonymous namespace
67 void SourceCoverageViewText::renderViewHeader(raw_ostream
&) {}
69 void SourceCoverageViewText::renderViewFooter(raw_ostream
&) {}
71 void SourceCoverageViewText::renderSourceName(raw_ostream
&OS
, bool WholeFile
) {
72 getOptions().colored_ostream(OS
, raw_ostream::CYAN
) << getSourceName()
76 void SourceCoverageViewText::renderLinePrefix(raw_ostream
&OS
,
78 for (unsigned I
= 0; I
< ViewDepth
; ++I
)
82 void SourceCoverageViewText::renderLineSuffix(raw_ostream
&, unsigned) {}
84 void SourceCoverageViewText::renderViewDivider(raw_ostream
&OS
,
86 assert(ViewDepth
!= 0 && "Cannot render divider at top level");
87 renderLinePrefix(OS
, ViewDepth
- 1);
89 unsigned Length
= getDividerWidth(getOptions());
90 for (unsigned I
= 0; I
< Length
; ++I
)
95 void SourceCoverageViewText::renderLine(raw_ostream
&OS
, LineRef L
,
96 const LineCoverageStats
&LCS
,
97 unsigned ExpansionCol
,
99 StringRef Line
= L
.Line
;
100 unsigned LineNumber
= L
.LineNo
;
101 auto *WrappedSegment
= LCS
.getWrappedSegment();
102 CoverageSegmentArray Segments
= LCS
.getLineSegments();
104 Optional
<raw_ostream::Colors
> Highlight
;
105 SmallVector
<std::pair
<unsigned, unsigned>, 2> HighlightedRanges
;
107 // The first segment overlaps from a previous line, so we treat it specially.
108 if (WrappedSegment
&& !WrappedSegment
->IsGapRegion
&&
109 WrappedSegment
->HasCount
&& WrappedSegment
->Count
== 0)
110 Highlight
= raw_ostream::RED
;
112 // Output each segment of the line, possibly highlighted.
114 for (const auto *S
: Segments
) {
115 unsigned End
= std::min(S
->Col
, static_cast<unsigned>(Line
.size()) + 1);
116 colored_ostream(OS
, Highlight
? *Highlight
: raw_ostream::SAVEDCOLOR
,
117 getOptions().Colors
&& Highlight
, /*Bold=*/false,
119 << Line
.substr(Col
- 1, End
- Col
);
120 if (getOptions().Debug
&& Highlight
)
121 HighlightedRanges
.push_back(std::make_pair(Col
, End
));
123 if ((!S
->IsGapRegion
|| (Highlight
&& *Highlight
== raw_ostream::RED
)) &&
124 S
->HasCount
&& S
->Count
== 0)
125 Highlight
= raw_ostream::RED
;
126 else if (Col
== ExpansionCol
)
127 Highlight
= raw_ostream::CYAN
;
132 // Show the rest of the line.
133 colored_ostream(OS
, Highlight
? *Highlight
: raw_ostream::SAVEDCOLOR
,
134 getOptions().Colors
&& Highlight
, /*Bold=*/false, /*BG=*/true)
135 << Line
.substr(Col
- 1, Line
.size() - Col
+ 1);
138 if (getOptions().Debug
) {
139 for (const auto &Range
: HighlightedRanges
)
140 errs() << "Highlighted line " << LineNumber
<< ", " << Range
.first
141 << " -> " << Range
.second
<< '\n';
143 errs() << "Highlighted line " << LineNumber
<< ", " << Col
<< " -> ?\n";
147 void SourceCoverageViewText::renderLineCoverageColumn(
148 raw_ostream
&OS
, const LineCoverageStats
&Line
) {
149 if (!Line
.isMapped()) {
150 OS
.indent(LineCoverageColumnWidth
) << '|';
153 std::string C
= formatCount(Line
.getExecutionCount());
154 OS
.indent(LineCoverageColumnWidth
- C
.size());
155 colored_ostream(OS
, raw_ostream::MAGENTA
,
156 Line
.hasMultipleRegions() && getOptions().Colors
)
161 void SourceCoverageViewText::renderLineNumberColumn(raw_ostream
&OS
,
163 SmallString
<32> Buffer
;
164 raw_svector_ostream
BufferOS(Buffer
);
166 auto Str
= BufferOS
.str();
167 // Trim and align to the right.
168 Str
= Str
.substr(0, std::min(Str
.size(), (size_t)LineNumberColumnWidth
));
169 OS
.indent(LineNumberColumnWidth
- Str
.size()) << Str
<< '|';
172 void SourceCoverageViewText::renderRegionMarkers(raw_ostream
&OS
,
173 const LineCoverageStats
&Line
,
174 unsigned ViewDepth
) {
175 renderLinePrefix(OS
, ViewDepth
);
176 OS
.indent(getCombinedColumnWidth(getOptions()));
178 CoverageSegmentArray Segments
= Line
.getLineSegments();
180 // Just consider the segments which start *and* end on this line.
181 if (Segments
.size() > 1)
182 Segments
= Segments
.drop_back();
184 unsigned PrevColumn
= 1;
185 for (const auto *S
: Segments
) {
186 if (!S
->IsRegionEntry
)
188 if (S
->Count
== Line
.getExecutionCount())
190 // Skip to the new region.
191 if (S
->Col
> PrevColumn
)
192 OS
.indent(S
->Col
- PrevColumn
);
193 PrevColumn
= S
->Col
+ 1;
194 std::string C
= formatCount(S
->Count
);
195 PrevColumn
+= C
.size();
198 if (getOptions().Debug
)
199 errs() << "Marker at " << S
->Line
<< ":" << S
->Col
<< " = "
200 << formatCount(S
->Count
) << "\n";
205 void SourceCoverageViewText::renderExpansionSite(raw_ostream
&OS
, LineRef L
,
206 const LineCoverageStats
&LCS
,
207 unsigned ExpansionCol
,
208 unsigned ViewDepth
) {
209 renderLinePrefix(OS
, ViewDepth
);
210 OS
.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth
== 0 ? 0 : 1));
211 renderLine(OS
, L
, LCS
, ExpansionCol
, ViewDepth
);
214 void SourceCoverageViewText::renderExpansionView(raw_ostream
&OS
,
216 unsigned ViewDepth
) {
217 // Render the child subview.
218 if (getOptions().Debug
)
219 errs() << "Expansion at line " << ESV
.getLine() << ", " << ESV
.getStartCol()
220 << " -> " << ESV
.getEndCol() << '\n';
221 ESV
.View
->print(OS
, /*WholeFile=*/false, /*ShowSourceName=*/false,
222 /*ShowTitle=*/false, ViewDepth
+ 1);
225 void SourceCoverageViewText::renderInstantiationView(raw_ostream
&OS
,
226 InstantiationView
&ISV
,
227 unsigned ViewDepth
) {
228 renderLinePrefix(OS
, ViewDepth
);
231 getOptions().colored_ostream(OS
, raw_ostream::RED
)
232 << "Unexecuted instantiation: " << ISV
.FunctionName
<< "\n";
234 ISV
.View
->print(OS
, /*WholeFile=*/false, /*ShowSourceName=*/true,
235 /*ShowTitle=*/false, ViewDepth
);
238 void SourceCoverageViewText::renderTitle(raw_ostream
&OS
, StringRef Title
) {
239 if (getOptions().hasProjectTitle())
240 getOptions().colored_ostream(OS
, raw_ostream::CYAN
)
241 << getOptions().ProjectTitle
<< "\n";
243 getOptions().colored_ostream(OS
, raw_ostream::CYAN
) << Title
<< "\n";
245 if (getOptions().hasCreatedTime())
246 getOptions().colored_ostream(OS
, raw_ostream::CYAN
)
247 << getOptions().CreatedTimeStr
<< "\n";
250 void SourceCoverageViewText::renderTableHeader(raw_ostream
&, unsigned,