llvm-cov: Rename a variable and clean up its usage
[oota-llvm.git] / tools / llvm-cov / SourceCoverageView.cpp
1 //===- SourceCoverageView.cpp - Code coverage view for source code --------===//
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 // This class implements rendering for code coverage of source code.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "SourceCoverageView.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/Support/LineIterator.h"
17
18 using namespace llvm;
19
20 void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
21                                     ArrayRef<HighlightRange> Ranges) {
22   if (Ranges.empty()) {
23     OS << Line << "\n";
24     return;
25   }
26   if (Line.empty())
27     Line = " ";
28
29   unsigned PrevColumnStart = 0;
30   unsigned Start = 1;
31   for (const auto &Range : Ranges) {
32     if (PrevColumnStart == Range.ColumnStart)
33       continue;
34
35     // Show the unhighlighted part
36     unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
37     OS << Line.substr(Start - 1, ColumnStart - Start);
38
39     // Show the highlighted part
40     auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
41                                                           : raw_ostream::CYAN;
42     OS.changeColor(Color, false, true);
43     unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);
44     OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
45     Start = ColumnEnd;
46     OS.resetColor();
47   }
48
49   // Show the rest of the line
50   OS << Line.substr(Start - 1, Line.size() - Start + 1);
51   OS << "\n";
52
53   if (Options.Debug) {
54     for (const auto &Range : Ranges) {
55       errs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
56              << " -> ";
57       if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
58         errs() << "?\n";
59       } else {
60         errs() << Range.ColumnEnd << "\n";
61       }
62     }
63   }
64 }
65
66 void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) {
67   for (unsigned I = 0; I < Level; ++I)
68     OS << "  |";
69 }
70
71 void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length,
72                                            raw_ostream &OS) {
73   assert(Level != 0 && "Cannot render divider at top level");
74   renderIndent(OS, Level - 1);
75   OS.indent(2);
76   for (unsigned I = 0; I < Length; ++I)
77     OS << "-";
78 }
79
80 void
81 SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
82                                              const LineCoverageInfo &Line) {
83   if (!Line.isMapped()) {
84     OS.indent(LineCoverageColumnWidth) << '|';
85     return;
86   }
87   SmallString<32> Buffer;
88   raw_svector_ostream BufferOS(Buffer);
89   BufferOS << Line.ExecutionCount;
90   auto Str = BufferOS.str();
91   // Trim
92   Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
93   // Align to the right
94   OS.indent(LineCoverageColumnWidth - Str.size());
95   colored_ostream(OS, raw_ostream::MAGENTA,
96                   Line.hasMultipleRegions() && Options.Colors)
97       << Str;
98   OS << '|';
99 }
100
101 void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
102                                                 unsigned LineNo) {
103   SmallString<32> Buffer;
104   raw_svector_ostream BufferOS(Buffer);
105   BufferOS << LineNo;
106   auto Str = BufferOS.str();
107   // Trim and align to the right
108   Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
109   OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
110 }
111
112 void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
113                                              ArrayRef<RegionMarker> Regions) {
114   SmallString<32> Buffer;
115   raw_svector_ostream BufferOS(Buffer);
116
117   unsigned PrevColumn = 1;
118   for (const auto &Region : Regions) {
119     // Skip to the new region
120     if (Region.Column > PrevColumn)
121       OS.indent(Region.Column - PrevColumn);
122     PrevColumn = Region.Column + 1;
123     BufferOS << Region.ExecutionCount;
124     StringRef Str = BufferOS.str();
125     // Trim the execution count
126     Str = Str.substr(0, std::min(Str.size(), (size_t)7));
127     PrevColumn += Str.size();
128     OS << '^' << Str;
129     Buffer.clear();
130   }
131   OS << "\n";
132
133   if (Options.Debug) {
134     for (const auto &Region : Regions) {
135       errs() << "Marker at " << Region.Line << ":" << Region.Column << " = "
136              << Region.ExecutionCount << "\n";
137     }
138   }
139 }
140
141 /// \brief Insert a new highlighting range into the line's highlighting ranges
142 /// Return line's new highlighting ranges in result.
143 static void insertHighlightRange(
144     ArrayRef<SourceCoverageView::HighlightRange> Ranges,
145     SourceCoverageView::HighlightRange RangeToInsert,
146     SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
147   Result.clear();
148   size_t I = 0;
149   auto E = Ranges.size();
150   for (; I < E; ++I) {
151     if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
152       const auto &Range = Ranges[I];
153       bool NextRangeContainsInserted = false;
154       // If the next range starts before the inserted range, move the end of the
155       // next range to the start of the inserted range.
156       if (Range.ColumnStart < RangeToInsert.ColumnStart) {
157         if (RangeToInsert.ColumnStart != Range.ColumnStart)
158           Result.push_back(SourceCoverageView::HighlightRange(
159               Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
160               Range.Kind));
161         // If the next range also ends after the inserted range, keep this range
162         // and create a new range that starts at the inserted range and ends
163         // at the next range later.
164         if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
165           NextRangeContainsInserted = true;
166       }
167       if (!NextRangeContainsInserted) {
168         ++I;
169         // Ignore ranges that are contained in inserted range
170         while (I < E && RangeToInsert.contains(Ranges[I]))
171           ++I;
172       }
173       break;
174     }
175     Result.push_back(Ranges[I]);
176   }
177   Result.push_back(RangeToInsert);
178   // If the next range starts before the inserted range end, move the start
179   // of the next range to the end of the inserted range.
180   if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
181     const auto &Range = Ranges[I];
182     if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
183       Result.push_back(SourceCoverageView::HighlightRange(
184           Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
185     ++I;
186   }
187   // Add the remaining ranges that are located after the inserted range
188   for (; I < E; ++I)
189     Result.push_back(Ranges[I]);
190 }
191
192 void SourceCoverageView::sortChildren() {
193   for (auto &I : Children)
194     I->sortChildren();
195   std::sort(Children.begin(), Children.end(),
196             [](const std::unique_ptr<SourceCoverageView> &LHS,
197                const std::unique_ptr<SourceCoverageView> &RHS) {
198     return LHS->ExpansionRegion < RHS->ExpansionRegion;
199   });
200 }
201
202 SourceCoverageView::HighlightRange
203 SourceCoverageView::getExpansionHighlightRange() const {
204   return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
205                         ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
206 }
207
208 template <typename T>
209 ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
210                             unsigned LineNo) {
211   auto PrevIdx = CurrentIdx;
212   auto E = Items.size();
213   while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
214     ++CurrentIdx;
215   return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
216 }
217
218 ArrayRef<std::unique_ptr<SourceCoverageView>>
219 gatherLineSubViews(size_t &CurrentIdx,
220                    ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
221                    unsigned LineNo) {
222   auto PrevIdx = CurrentIdx;
223   auto E = Items.size();
224   while (CurrentIdx < E &&
225          Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
226     ++CurrentIdx;
227   return Items.slice(PrevIdx, CurrentIdx - PrevIdx);
228 }
229
230 void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) {
231   // Make sure that the children are in sorted order.
232   sortChildren();
233
234   SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
235   size_t CurrentChild = 0;
236   size_t CurrentHighlightRange = 0;
237   size_t CurrentRegionMarker = 0;
238
239   line_iterator Lines(File);
240   // Advance the line iterator to the first line.
241   while (Lines.line_number() < LineOffset)
242     ++Lines;
243
244   // The width of the leading columns
245   unsigned CombinedColumnWidth =
246       (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
247       (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
248   // The width of the line that is used to divide between the view and the
249   // subviews.
250   unsigned DividerWidth = CombinedColumnWidth + 4;
251
252   for (size_t I = 0, E = LineStats.size(); I < E; ++I) {
253     unsigned LineNo = I + LineOffset;
254
255     // Gather the child subviews that are visible on this line.
256     auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
257
258     renderIndent(OS, IndentLevel);
259     if (Options.ShowLineStats)
260       renderLineCoverageColumn(OS, LineStats[I]);
261     if (Options.ShowLineNumbers)
262       renderLineNumberColumn(OS, LineNo);
263
264     // Gather highlighting ranges.
265     auto LineHighlightRanges =
266         gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
267     auto LineRanges = LineHighlightRanges;
268     // Highlight the expansion range if there is an expansion subview on this
269     // line.
270     if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
271         Options.Colors) {
272       insertHighlightRange(LineHighlightRanges,
273                            LineSubViews.front()->getExpansionHighlightRange(),
274                            AdjustedLineHighlightRanges);
275       LineRanges = AdjustedLineHighlightRanges;
276     }
277
278     // Display the source code for the current line.
279     StringRef Line = *Lines;
280     // Check if the line is empty, as line_iterator skips blank lines.
281     if (LineNo < Lines.line_number())
282       Line = "";
283     else if (!Lines.is_at_eof())
284       ++Lines;
285     renderLine(OS, Line, LineRanges);
286
287     // Show the region markers.
288     bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
289                        LineStats[I].hasMultipleRegions();
290     auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
291     if (ShowMarkers && !LineMarkers.empty()) {
292       renderIndent(OS, IndentLevel);
293       OS.indent(CombinedColumnWidth);
294       renderRegionMarkers(OS, LineMarkers);
295     }
296
297     // Show the line's expanded child subviews.
298     bool FirstChildExpansion = true;
299     if (LineSubViews.empty())
300       continue;
301     unsigned NestedIndent = IndentLevel + 1;
302     renderViewDivider(NestedIndent, DividerWidth, OS);
303     OS << "\n";
304     for (const auto &Child : LineSubViews) {
305       // If this subview shows a function instantiation, render the function's
306       // name.
307       if (Child->isInstantiationSubView()) {
308         renderIndent(OS, NestedIndent);
309         OS << ' ';
310         Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
311                                                        << ":";
312         OS << "\n";
313       } else {
314         if (!FirstChildExpansion) {
315           // Re-render the current line and highlight the expansion range for
316           // this
317           // subview.
318           insertHighlightRange(LineHighlightRanges,
319                                Child->getExpansionHighlightRange(),
320                                AdjustedLineHighlightRanges);
321           renderIndent(OS, IndentLevel);
322           OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
323           renderLine(OS, Line, AdjustedLineHighlightRanges);
324           renderViewDivider(NestedIndent, DividerWidth, OS);
325           OS << "\n";
326         } else
327           FirstChildExpansion = false;
328       }
329       // Render the child subview
330       Child->render(OS, NestedIndent);
331       renderViewDivider(NestedIndent, DividerWidth, OS);
332       OS << "\n";
333     }
334   }
335 }
336
337 void SourceCoverageView::setUpVisibleRange(SourceCoverageDataManager &Data) {
338   auto CountedRegions = Data.getSourceRegions();
339   if (!CountedRegions.size())
340     return;
341
342   unsigned Start = CountedRegions.front().LineStart, End = 0;
343   for (const auto &CR : CountedRegions) {
344     Start = std::min(Start, CR.LineStart);
345     End = std::max(End, CR.LineEnd);
346   }
347   LineOffset = Start;
348   LineStats.resize(End - Start + 1);
349 }
350
351 void
352 SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
353   auto CountedRegions = Data.getSourceRegions();
354   for (const auto &CR : CountedRegions) {
355     if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion) {
356       // Reset the line stats for skipped regions.
357       for (unsigned Line = CR.LineStart; Line <= CR.LineEnd;
358            ++Line)
359         LineStats[Line - LineOffset] = LineCoverageInfo();
360       continue;
361     }
362     LineStats[CR.LineStart - LineOffset].addRegionStartCount(CR.ExecutionCount);
363     for (unsigned Line = CR.LineStart + 1; Line <= CR.LineEnd; ++Line)
364       LineStats[Line - LineOffset].addRegionCount(CR.ExecutionCount);
365   }
366 }
367
368 void
369 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
370   auto CountedRegions = Data.getSourceRegions();
371   std::vector<bool> AlreadyHighlighted;
372   AlreadyHighlighted.resize(CountedRegions.size(), false);
373
374   for (size_t I = 0, S = CountedRegions.size(); I < S; ++I) {
375     const auto &CR = CountedRegions[I];
376     if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion ||
377         CR.ExecutionCount != 0)
378       continue;
379     if (AlreadyHighlighted[I])
380       continue;
381     for (size_t J = 0; J < S; ++J) {
382       if (CR.contains(CountedRegions[J])) {
383         AlreadyHighlighted[J] = true;
384       }
385     }
386     if (CR.LineStart == CR.LineEnd) {
387       HighlightRanges.push_back(HighlightRange(
388           CR.LineStart, CR.ColumnStart, CR.ColumnEnd));
389       continue;
390     }
391     HighlightRanges.push_back(
392         HighlightRange(CR.LineStart, CR.ColumnStart,
393                        std::numeric_limits<unsigned>::max()));
394     HighlightRanges.push_back(
395         HighlightRange(CR.LineEnd, 1, CR.ColumnEnd));
396     for (unsigned Line = CR.LineStart + 1; Line < CR.LineEnd;
397          ++Line) {
398       HighlightRanges.push_back(
399           HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
400     }
401   }
402
403   std::sort(HighlightRanges.begin(), HighlightRanges.end());
404 }
405
406 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
407   for (const auto &CR : Data.getSourceRegions())
408     if (CR.Kind != coverage::CounterMappingRegion::SkippedRegion)
409       Markers.push_back(
410           RegionMarker(CR.LineStart, CR.ColumnStart, CR.ExecutionCount));
411 }
412
413 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
414   setUpVisibleRange(Data);
415   if (Options.ShowLineStats)
416     createLineCoverageInfo(Data);
417   if (Options.Colors)
418     createHighlightRanges(Data);
419   if (Options.ShowRegionMarkers)
420     createRegionMarkers(Data);
421 }