xref: /llvm-project/llvm/tools/llvm-cov/SourceCoverageViewText.cpp (revision d36b47c481c213f5b77fd2755b800ff9604866c0)
1 //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file This file implements the text-based coverage renderer.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "SourceCoverageViewText.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringExtras.h"
18 
19 using namespace llvm;
20 
21 Expected<CoveragePrinter::OwnedStream>
22 CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
23   return createOutputStream(Path, "txt", InToplevel);
24 }
25 
26 void CoveragePrinterText::closeViewFile(OwnedStream OS) {
27   OS->operator<<('\n');
28 }
29 
30 Error CoveragePrinterText::createIndexFile(ArrayRef<StringRef> SourceFiles) {
31   auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
32   if (Error E = OSOrErr.takeError())
33     return E;
34   auto OS = std::move(OSOrErr.get());
35   raw_ostream &OSRef = *OS.get();
36 
37   for (StringRef SF : SourceFiles)
38     OSRef << getOutputPath(SF, "txt", /*InToplevel=*/false) << '\n';
39 
40   return Error::success();
41 }
42 
43 namespace {
44 
45 static const unsigned LineCoverageColumnWidth = 7;
46 static const unsigned LineNumberColumnWidth = 5;
47 
48 /// \brief Get the width of the leading columns.
49 unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
50   return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
51          (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
52 }
53 
54 /// \brief The width of the line that is used to divide between the view and
55 /// the subviews.
56 unsigned getDividerWidth(const CoverageViewOptions &Opts) {
57   return getCombinedColumnWidth(Opts) + 4;
58 }
59 
60 } // anonymous namespace
61 
62 void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
63 
64 void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
65 
66 void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile,
67                                               unsigned FirstUncoveredLineNo) {
68   getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
69                                                       << ":\n";
70   if (WholeFile) {
71     getOptions().colored_ostream(OS, raw_ostream::CYAN)
72         << getOptions().ObjectFilename << ":\n";
73   }
74 }
75 
76 void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
77                                               unsigned ViewDepth) {
78   for (unsigned I = 0; I < ViewDepth; ++I)
79     OS << "  |";
80 }
81 
82 void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
83 
84 void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
85                                                unsigned ViewDepth) {
86   assert(ViewDepth != 0 && "Cannot render divider at top level");
87   renderLinePrefix(OS, ViewDepth - 1);
88   OS.indent(2);
89   unsigned Length = getDividerWidth(getOptions());
90   for (unsigned I = 0; I < Length; ++I)
91     OS << '-';
92   OS << '\n';
93 }
94 
95 void SourceCoverageViewText::renderLine(
96     raw_ostream &OS, LineRef L,
97     const coverage::CoverageSegment *WrappedSegment,
98     CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
99   StringRef Line = L.Line;
100   unsigned LineNumber = L.LineNo;
101 
102   Optional<raw_ostream::Colors> Highlight;
103   SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
104 
105   // The first segment overlaps from a previous line, so we treat it specially.
106   if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
107     Highlight = raw_ostream::RED;
108 
109   // Output each segment of the line, possibly highlighted.
110   unsigned Col = 1;
111   for (const auto *S : Segments) {
112     unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
113     colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
114                     getOptions().Colors && Highlight, /*Bold=*/false,
115                     /*BG=*/true)
116         << Line.substr(Col - 1, End - Col);
117     if (getOptions().Debug && Highlight)
118       HighlightedRanges.push_back(std::make_pair(Col, End));
119     Col = End;
120     if (Col == ExpansionCol)
121       Highlight = raw_ostream::CYAN;
122     else if (S->HasCount && S->Count == 0)
123       Highlight = raw_ostream::RED;
124     else
125       Highlight = None;
126   }
127 
128   // Show the rest of the line.
129   colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
130                   getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
131       << Line.substr(Col - 1, Line.size() - Col + 1);
132   OS << '\n';
133 
134   if (getOptions().Debug) {
135     for (const auto &Range : HighlightedRanges)
136       errs() << "Highlighted line " << LineNumber << ", " << Range.first
137              << " -> " << Range.second << '\n';
138     if (Highlight)
139       errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
140   }
141 }
142 
143 void SourceCoverageViewText::renderLineCoverageColumn(
144     raw_ostream &OS, const LineCoverageStats &Line) {
145   if (!Line.isMapped()) {
146     OS.indent(LineCoverageColumnWidth) << '|';
147     return;
148   }
149   std::string C = formatCount(Line.ExecutionCount);
150   OS.indent(LineCoverageColumnWidth - C.size());
151   colored_ostream(OS, raw_ostream::MAGENTA,
152                   Line.hasMultipleRegions() && getOptions().Colors)
153       << C;
154   OS << '|';
155 }
156 
157 void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
158                                                     unsigned LineNo) {
159   SmallString<32> Buffer;
160   raw_svector_ostream BufferOS(Buffer);
161   BufferOS << LineNo;
162   auto Str = BufferOS.str();
163   // Trim and align to the right.
164   Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
165   OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
166 }
167 
168 void SourceCoverageViewText::renderRegionMarkers(
169     raw_ostream &OS, CoverageSegmentArray Segments, unsigned ViewDepth) {
170   renderLinePrefix(OS, ViewDepth);
171   OS.indent(getCombinedColumnWidth(getOptions()));
172 
173   unsigned PrevColumn = 1;
174   for (const auto *S : Segments) {
175     if (!S->IsRegionEntry)
176       continue;
177     // Skip to the new region.
178     if (S->Col > PrevColumn)
179       OS.indent(S->Col - PrevColumn);
180     PrevColumn = S->Col + 1;
181     std::string C = formatCount(S->Count);
182     PrevColumn += C.size();
183     OS << '^' << C;
184   }
185   OS << '\n';
186 
187   if (getOptions().Debug)
188     for (const auto *S : Segments)
189       errs() << "Marker at " << S->Line << ":" << S->Col << " = "
190              << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
191 }
192 
193 void SourceCoverageViewText::renderExpansionSite(
194     raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
195     CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
196   renderLinePrefix(OS, ViewDepth);
197   OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
198   renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
199 }
200 
201 void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
202                                                  ExpansionView &ESV,
203                                                  unsigned ViewDepth) {
204   // Render the child subview.
205   if (getOptions().Debug)
206     errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
207            << " -> " << ESV.getEndCol() << '\n';
208   ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
209                   ViewDepth + 1);
210 }
211 
212 void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
213                                                      InstantiationView &ISV,
214                                                      unsigned ViewDepth) {
215   renderLinePrefix(OS, ViewDepth);
216   OS << ' ';
217   ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
218 }
219 
220 void SourceCoverageViewText::renderCellInTitle(raw_ostream &OS,
221                                                StringRef CellText) {
222   if (getOptions().hasProjectTitle())
223     getOptions().colored_ostream(OS, raw_ostream::CYAN)
224         << getOptions().ProjectTitle << "\n";
225 
226   getOptions().colored_ostream(OS, raw_ostream::CYAN) << CellText << "\n";
227 
228   if (getOptions().hasCreatedTime())
229     getOptions().colored_ostream(OS, raw_ostream::CYAN)
230         << getOptions().CreatedTimeStr << "\n";
231 }
232 
233 void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned) {}
234