Coverage Report

Created: 2017-09-21 03:39

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- SourceCoverageViewHTML.cpp - A html 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 html coverage renderer.
11
///
12
//===----------------------------------------------------------------------===//
13
14
#include "CoverageReport.h"
15
#include "SourceCoverageViewHTML.h"
16
#include "llvm/ADT/Optional.h"
17
#include "llvm/ADT/SmallString.h"
18
#include "llvm/ADT/StringExtras.h"
19
#include "llvm/Support/FileSystem.h"
20
#include "llvm/Support/Format.h"
21
#include "llvm/Support/Path.h"
22
23
using namespace llvm;
24
25
namespace {
26
27
// Return a string with the special characters in \p Str escaped.
28
1.64k
std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
29
1.64k
  std::string Result;
30
1.64k
  unsigned ColNum = 0; // Record the column number.
31
66.4k
  for (char C : Str) {
32
66.4k
    ++ColNum;
33
66.4k
    if (C == '&')
34
83
      Result += "&";
35
66.3k
    else 
if (66.3k
C == '<'66.3k
)
36
1.86k
      Result += "&lt;";
37
64.4k
    else 
if (64.4k
C == '>'64.4k
)
38
1.88k
      Result += "&gt;";
39
62.6k
    else 
if (62.6k
C == '\"'62.6k
)
40
30
      Result += "&quot;";
41
62.5k
    else 
if (62.5k
C == '\n' || 62.5k
C == '\r'62.5k
) {
42
0
      Result += C;
43
0
      ColNum = 0;
44
62.5k
    } else 
if (62.5k
C == '\t'62.5k
) {
45
6
      // Replace '\t' with TabSize spaces.
46
6
      unsigned NumSpaces = Opts.TabSize - (--ColNum % Opts.TabSize);
47
19
      for (unsigned I = 0; 
I < NumSpaces19
;
++I13
)
48
13
        Result += "&nbsp;";
49
6
      ColNum += NumSpaces;
50
6
    } else
51
62.5k
      Result += C;
52
66.4k
  }
53
1.64k
  return Result;
54
1.64k
}
55
56
// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
57
std::string tag(const std::string &Name, const std::string &Str,
58
2.86k
                const std::string &ClassName = "") {
59
2.86k
  std::string Tag = "<" + Name;
60
2.86k
  if (ClassName != "")
61
1.60k
    Tag += " class='" + ClassName + "'";
62
2.86k
  return Tag + ">" + Str + "</" + Name + ">";
63
2.86k
}
64
65
// Create an anchor to \p Link with the label \p Str.
66
std::string a(const std::string &Link, const std::string &Str,
67
760
              const std::string &TargetName = "") {
68
760
  std::string Name = TargetName.empty() ? 
""30
:
("name='" + TargetName + "' ")730
;
69
760
  return "<a " + Name + "href='" + Link + "'>" + Str + "</a>";
70
760
}
71
72
const char *BeginHeader =
73
  "<head>"
74
    "<meta name='viewport' content='width=device-width,initial-scale=1'>"
75
    "<meta charset='UTF-8'>";
76
77
const char *CSSForCoverage =
78
    R"(.red {
79
  background-color: #ffd0d0;
80
}
81
.cyan {
82
  background-color: cyan;
83
}
84
body {
85
  font-family: -apple-system, sans-serif;
86
}
87
pre {
88
  margin-top: 0px !important;
89
  margin-bottom: 0px !important;
90
}
91
.source-name-title {
92
  padding: 5px 10px;
93
  border-bottom: 1px solid #dbdbdb;
94
  background-color: #eee;
95
  line-height: 35px;
96
}
97
.centered {
98
  display: table;
99
  margin-left: left;
100
  margin-right: auto;
101
  border: 1px solid #dbdbdb;
102
  border-radius: 3px;
103
}
104
.expansion-view {
105
  background-color: rgba(0, 0, 0, 0);
106
  margin-left: 0px;
107
  margin-top: 5px;
108
  margin-right: 5px;
109
  margin-bottom: 5px;
110
  border: 1px solid #dbdbdb;
111
  border-radius: 3px;
112
}
113
table {
114
  border-collapse: collapse;
115
}
116
.light-row {
117
  background: #ffffff;
118
  border: 1px solid #dbdbdb;
119
}
120
.column-entry {
121
  text-align: right;
122
}
123
.column-entry-left {
124
  text-align: left;
125
}
126
.column-entry-yellow {
127
  text-align: right;
128
  background-color: #ffffd0;
129
}
130
.column-entry-red {
131
  text-align: right;
132
  background-color: #ffd0d0;
133
}
134
.column-entry-green {
135
  text-align: right;
136
  background-color: #d0ffd0;
137
}
138
.line-number {
139
  text-align: right;
140
  color: #aaa;
141
}
142
.covered-line {
143
  text-align: right;
144
  color: #0080ff;
145
}
146
.uncovered-line {
147
  text-align: right;
148
  color: #ff3300;
149
}
150
.tooltip {
151
  position: relative;
152
  display: inline;
153
  background-color: #b3e6ff;
154
  text-decoration: none;
155
}
156
.tooltip span.tooltip-content {
157
  position: absolute;
158
  width: 100px;
159
  margin-left: -50px;
160
  color: #FFFFFF;
161
  background: #000000;
162
  height: 30px;
163
  line-height: 30px;
164
  text-align: center;
165
  visibility: hidden;
166
  border-radius: 6px;
167
}
168
.tooltip span.tooltip-content:after {
169
  content: '';
170
  position: absolute;
171
  top: 100%;
172
  left: 50%;
173
  margin-left: -8px;
174
  width: 0; height: 0;
175
  border-top: 8px solid #000000;
176
  border-right: 8px solid transparent;
177
  border-left: 8px solid transparent;
178
}
179
:hover.tooltip span.tooltip-content {
180
  visibility: visible;
181
  opacity: 0.8;
182
  bottom: 30px;
183
  left: 50%;
184
  z-index: 999;
185
}
186
th, td {
187
  vertical-align: top;
188
  padding: 2px 5px;
189
  border-collapse: collapse;
190
  border-right: solid 1px #eee;
191
  border-left: solid 1px #eee;
192
}
193
td:first-child {
194
  border-left: none;
195
}
196
td:last-child {
197
  border-right: none;
198
}
199
)";
200
201
const char *EndHeader = "</head>";
202
203
const char *BeginCenteredDiv = "<div class='centered'>";
204
205
const char *EndCenteredDiv = "</div>";
206
207
const char *BeginSourceNameDiv = "<div class='source-name-title'>";
208
209
const char *EndSourceNameDiv = "</div>";
210
211
const char *BeginCodeTD = "<td class='code'>";
212
213
const char *EndCodeTD = "</td>";
214
215
const char *BeginPre = "<pre>";
216
217
const char *EndPre = "</pre>";
218
219
const char *BeginExpansionDiv = "<div class='expansion-view'>";
220
221
const char *EndExpansionDiv = "</div>";
222
223
const char *BeginTable = "<table>";
224
225
const char *EndTable = "</table>";
226
227
const char *ProjectTitleTag = "h1";
228
229
const char *ReportTitleTag = "h2";
230
231
const char *CreatedTimeTag = "h4";
232
233
18
std::string getPathToStyle(StringRef ViewPath) {
234
18
  std::string PathToStyle = "";
235
18
  std::string PathSep = sys::path::get_separator();
236
18
  unsigned NumSeps = ViewPath.count(PathSep);
237
30
  for (unsigned I = 0, E = NumSeps; 
I < E30
;
++I12
)
238
12
    PathToStyle += ".." + PathSep;
239
18
  return PathToStyle + "style.css";
240
18
}
241
242
void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts,
243
23
                 const std::string &PathToStyle = "") {
244
23
  OS << "<!doctype html>"
245
23
        "<html>"
246
23
     << BeginHeader;
247
23
248
23
  // Link to a stylesheet if one is available. Otherwise, use the default style.
249
23
  if (PathToStyle.empty())
250
5
    OS << "<style>" << CSSForCoverage << "</style>";
251
23
  else
252
18
    OS << "<link rel='stylesheet' type='text/css' href='"
253
18
       << escape(PathToStyle, Opts) << "'>";
254
23
255
23
  OS << EndHeader << "<body>";
256
23
}
257
258
23
void emitEpilog(raw_ostream &OS) {
259
23
  OS << "</body>"
260
23
     << "</html>";
261
23
}
262
263
} // anonymous namespace
264
265
Expected<CoveragePrinter::OwnedStream>
266
15
CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
267
15
  auto OSOrErr = createOutputStream(Path, "html", InToplevel);
268
15
  if (!OSOrErr)
269
0
    return OSOrErr;
270
15
271
15
  OwnedStream OS = std::move(OSOrErr.get());
272
15
273
15
  if (
!Opts.hasOutputDirectory()15
) {
274
5
    emitPrelude(*OS.get(), Opts);
275
15
  } else {
276
10
    std::string ViewPath = getOutputPath(Path, "html", InToplevel);
277
10
    emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath));
278
10
  }
279
15
280
15
  return std::move(OS);
281
15
}
282
283
15
void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
284
15
  emitEpilog(*OS.get());
285
15
}
286
287
/// Emit column labels for the table in the index.
288
static void emitColumnLabelsForIndex(raw_ostream &OS,
289
8
                                     const CoverageViewOptions &Opts) {
290
8
  SmallVector<std::string, 4> Columns;
291
8
  Columns.emplace_back(tag("td", "Filename", "column-entry-left"));
292
8
  Columns.emplace_back(tag("td", "Function Coverage", "column-entry"));
293
8
  if (Opts.ShowInstantiationSummary)
294
1
    Columns.emplace_back(tag("td", "Instantiation Coverage", "column-entry"));
295
8
  Columns.emplace_back(tag("td", "Line Coverage", "column-entry"));
296
8
  if (Opts.ShowRegionSummary)
297
8
    Columns.emplace_back(tag("td", "Region Coverage", "column-entry"));
298
8
  OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
299
8
}
300
301
std::string
302
CoveragePrinterHTML::buildLinkToFile(StringRef SF,
303
9
                                     const FileCoverageSummary &FCS) const {
304
9
  SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
305
9
  sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true);
306
9
  sys::path::native(LinkTextStr);
307
9
  std::string LinkText = escape(LinkTextStr, Opts);
308
9
  std::string LinkTarget =
309
9
      escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
310
9
  return a(LinkTarget, LinkText);
311
9
}
312
313
/// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is
314
/// false, link the summary to \p SF.
315
void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
316
                                          const FileCoverageSummary &FCS,
317
16
                                          bool IsTotals) const {
318
16
  SmallVector<std::string, 8> Columns;
319
16
320
16
  // Format a coverage triple and add the result to the list of columns.
321
16
  auto AddCoverageTripleToColumn = [&Columns](unsigned Hit, unsigned Total,
322
50
                                              float Pctg) {
323
50
    std::string S;
324
50
    {
325
50
      raw_string_ostream RSO{S};
326
50
      if (Total)
327
50
        RSO << format("%*.2f", 7, Pctg) << "% ";
328
50
      else
329
0
        RSO << "- ";
330
50
      RSO << '(' << Hit << '/' << Total << ')';
331
50
    }
332
50
    const char *CellClass = "column-entry-yellow";
333
50
    if (Hit == Total)
334
20
      CellClass = "column-entry-green";
335
30
    else 
if (30
Pctg < 80.030
)
336
14
      CellClass = "column-entry-red";
337
50
    Columns.emplace_back(tag("td", tag("pre", S), CellClass));
338
50
  };
339
16
340
16
  // Simplify the display file path, and wrap it in a link if requested.
341
16
  std::string Filename;
342
16
  if (
IsTotals16
) {
343
8
    Filename = "TOTALS";
344
16
  } else {
345
8
    Filename = buildLinkToFile(SF, FCS);
346
8
  }
347
16
348
16
  Columns.emplace_back(tag("td", tag("pre", Filename)));
349
16
  AddCoverageTripleToColumn(FCS.FunctionCoverage.getExecuted(),
350
16
                            FCS.FunctionCoverage.getNumFunctions(),
351
16
                            FCS.FunctionCoverage.getPercentCovered());
352
16
  if (Opts.ShowInstantiationSummary)
353
2
    AddCoverageTripleToColumn(FCS.InstantiationCoverage.getExecuted(),
354
2
                              FCS.InstantiationCoverage.getNumFunctions(),
355
2
                              FCS.InstantiationCoverage.getPercentCovered());
356
16
  AddCoverageTripleToColumn(FCS.LineCoverage.getCovered(),
357
16
                            FCS.LineCoverage.getNumLines(),
358
16
                            FCS.LineCoverage.getPercentCovered());
359
16
  if (Opts.ShowRegionSummary)
360
16
    AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
361
16
                              FCS.RegionCoverage.getNumRegions(),
362
16
                              FCS.RegionCoverage.getPercentCovered());
363
16
364
16
  OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
365
16
}
366
367
Error CoveragePrinterHTML::createIndexFile(
368
    ArrayRef<std::string> SourceFiles,
369
8
    const coverage::CoverageMapping &Coverage) {
370
8
  // Emit the default stylesheet.
371
8
  auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
372
8
  if (Error E = CSSOrErr.takeError())
373
0
    return E;
374
8
375
8
  OwnedStream CSS = std::move(CSSOrErr.get());
376
8
  CSS->operator<<(CSSForCoverage);
377
8
378
8
  // Emit a file index along with some coverage statistics.
379
8
  auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
380
8
  if (Error E = OSOrErr.takeError())
381
0
    return E;
382
8
  auto OS = std::move(OSOrErr.get());
383
8
  raw_ostream &OSRef = *OS.get();
384
8
385
8
  assert(Opts.hasOutputDirectory() && "No output directory for index file");
386
8
  emitPrelude(OSRef, Opts, getPathToStyle(""));
387
8
388
8
  // Emit some basic information about the coverage report.
389
8
  if (Opts.hasProjectTitle())
390
1
    OSRef << tag(ProjectTitleTag, escape(Opts.ProjectTitle, Opts));
391
8
  OSRef << tag(ReportTitleTag, "Coverage Report");
392
8
  if (Opts.hasCreatedTime())
393
8
    OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts));
394
8
395
8
  // Emit a link to some documentation.
396
8
  OSRef << tag("p", "Click " +
397
8
                        a("http://clang.llvm.org/docs/"
398
8
                          "SourceBasedCodeCoverage.html#interpreting-reports",
399
8
                          "here") +
400
8
                        " for information about interpreting this report.");
401
8
402
8
  // Emit a table containing links to reports for each file in the covmapping.
403
8
  // Exclude files which don't contain any regions.
404
8
  OSRef << BeginCenteredDiv << BeginTable;
405
8
  emitColumnLabelsForIndex(OSRef, Opts);
406
8
  FileCoverageSummary Totals("TOTALS");
407
8
  auto FileReports =
408
8
      CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Opts);
409
8
  bool EmptyFiles = false;
410
17
  for (unsigned I = 0, E = FileReports.size(); 
I < E17
;
++I9
) {
411
9
    if (FileReports[I].FunctionCoverage.getNumFunctions())
412
8
      emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
413
9
    else
414
1
      EmptyFiles = true;
415
9
  }
416
8
  emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
417
8
  OSRef << EndTable << EndCenteredDiv;
418
8
419
8
  // Emit links to files which don't contain any functions. These are normally
420
8
  // not very useful, but could be relevant for code which abuses the
421
8
  // preprocessor.
422
8
  if (
EmptyFiles8
) {
423
1
    OSRef << tag("p", "Files which contain no functions. (These "
424
1
                      "files contain code pulled into other files "
425
1
                      "by the preprocessor.)\n");
426
1
    OSRef << BeginCenteredDiv << BeginTable;
427
3
    for (unsigned I = 0, E = FileReports.size(); 
I < E3
;
++I2
)
428
2
      
if (2
!FileReports[I].FunctionCoverage.getNumFunctions()2
) {
429
1
        std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]);
430
1
        OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n';
431
1
      }
432
1
    OSRef << EndTable << EndCenteredDiv;
433
1
  }
434
8
435
8
  OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts));
436
8
  emitEpilog(OSRef);
437
8
438
8
  return Error::success();
439
8
}
440
441
21
void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
442
21
  OS << BeginCenteredDiv << BeginTable;
443
21
}
444
445
21
void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
446
21
  OS << EndTable << EndCenteredDiv;
447
21
}
448
449
21
void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) {
450
21
  OS << BeginSourceNameDiv << tag("pre", escape(getSourceName(), getOptions()))
451
21
     << EndSourceNameDiv;
452
21
}
453
454
751
void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
455
751
  OS << "<tr>";
456
751
}
457
458
751
void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
459
751
  // If this view has sub-views, renderLine() cannot close the view's cell.
460
751
  // Take care of it here, after all sub-views have been rendered.
461
751
  if (hasSubViews())
462
392
    OS << EndCodeTD;
463
751
  OS << "</tr>";
464
751
}
465
466
12
void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
467
12
  // The table-based output makes view dividers unnecessary.
468
12
}
469
470
void SourceCoverageViewHTML::renderLine(
471
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
472
730
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
473
730
  StringRef Line = L.Line;
474
730
  unsigned LineNo = L.LineNo;
475
730
476
730
  // Steps for handling text-escaping, highlighting, and tooltip creation:
477
730
  //
478
730
  // 1. Split the line into N+1 snippets, where N = |Segments|. The first
479
730
  //    snippet starts from Col=1 and ends at the start of the first segment.
480
730
  //    The last snippet starts at the last mapped column in the line and ends
481
730
  //    at the end of the line. Both are required but may be empty.
482
730
483
730
  SmallVector<std::string, 8> Snippets;
484
730
485
730
  unsigned LCol = 1;
486
1.56k
  auto Snip = [&](unsigned Start, unsigned Len) {
487
1.56k
    Snippets.push_back(Line.substr(Start, Len));
488
1.56k
    LCol += Len;
489
1.56k
  };
490
730
491
730
  Snip(LCol - 1, Segments.empty() ? 
0583
:
(Segments.front()->Col - 1)147
);
492
730
493
830
  for (unsigned I = 1, E = Segments.size(); 
I < E830
;
++I100
)
494
100
    Snip(LCol - 1, Segments[I]->Col - LCol);
495
730
496
730
  // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
497
730
  Snip(LCol - 1, Line.size() + 1 - LCol);
498
730
499
730
  // 2. Escape all of the snippets.
500
730
501
2.29k
  for (unsigned I = 0, E = Snippets.size(); 
I < E2.29k
;
++I1.56k
)
502
1.56k
    Snippets[I] = escape(Snippets[I], getOptions());
503
730
504
730
  // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment
505
730
  //    1 to set the highlight for snippet 2, segment 2 to set the highlight for
506
730
  //    snippet 3, and so on.
507
730
508
730
  Optional<std::string> Color;
509
730
  SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
510
66
  auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) {
511
66
    if (getOptions().Debug)
512
21
      HighlightedRanges.emplace_back(LC, RC);
513
66
    return tag("span", Snippet, Color.getValue());
514
66
  };
515
730
516
977
  auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
517
977
    return S && 
S->HasCount911
&&
S->Count == 0452
;
518
977
  };
519
730
520
730
  if (
CheckIfUncovered(WrappedSegment)730
) {
521
20
    Color = "red";
522
20
    if (!Snippets[0].empty())
523
13
      Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size());
524
20
  }
525
730
526
977
  for (unsigned I = 0, E = Segments.size(); 
I < E977
;
++I247
) {
527
247
    const auto *CurSeg = Segments[I];
528
247
    if (CurSeg->Col == ExpansionCol)
529
0
      Color = "cyan";
530
247
    else 
if (247
!CurSeg->IsGapRegion && 247
CheckIfUncovered(CurSeg)247
)
531
46
      Color = "red";
532
247
    else
533
201
      Color = None;
534
247
535
247
    if (Color.hasValue())
536
46
      Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col,
537
46
                                  CurSeg->Col + Snippets[I + 1].size());
538
247
  }
539
730
540
730
  if (
Color.hasValue() && 730
Segments.empty()20
)
541
7
    Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size());
542
730
543
730
  if (
getOptions().Debug730
) {
544
21
    for (const auto &Range : HighlightedRanges) {
545
21
      errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> ";
546
21
      if (Range.second == 0)
547
0
        errs() << "?";
548
21
      else
549
21
        errs() << Range.second;
550
21
      errs() << "\n";
551
21
    }
552
76
  }
553
730
554
730
  // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
555
730
  //    sub-line region count tooltips if needed.
556
730
557
730
  if (
shouldRenderRegionMarkers(Segments)730
) {
558
2
    // Just consider the segments which start *and* end on this line.
559
9
    for (unsigned I = 0, E = Segments.size() - 1; 
I < E9
;
++I7
) {
560
7
      const auto *CurSeg = Segments[I];
561
7
      if (!CurSeg->IsRegionEntry)
562
3
        continue;
563
4
564
4
      Snippets[I + 1] =
565
4
          tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
566
4
                                           "tooltip-content"),
567
4
              "tooltip");
568
4
569
4
      if (getOptions().Debug)
570
4
        errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = "
571
4
               << formatCount(CurSeg->Count) << "\n";
572
7
    }
573
2
  }
574
730
575
730
  OS << BeginCodeTD;
576
730
  OS << BeginPre;
577
730
  for (const auto &Snippet : Snippets)
578
1.56k
    OS << Snippet;
579
730
  OS << EndPre;
580
730
581
730
  // If there are no sub-views left to attach to this cell, end the cell.
582
730
  // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
583
730
  if (!hasSubViews())
584
342
    OS << EndCodeTD;
585
730
}
586
587
void SourceCoverageViewHTML::renderLineCoverageColumn(
588
702
    raw_ostream &OS, const LineCoverageStats &Line) {
589
702
  std::string Count = "";
590
702
  if (Line.isMapped())
591
238
    Count = tag("pre", formatCount(Line.ExecutionCount));
592
702
  std::string CoverageClass =
593
702
      (Line.ExecutionCount > 0) ? 
"covered-line"192
:
"uncovered-line"510
;
594
702
  OS << tag("td", Count, CoverageClass);
595
702
}
596
597
void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
598
730
                                                    unsigned LineNo) {
599
730
  std::string LineNoStr = utostr(uint64_t(LineNo));
600
730
  std::string TargetName = "L" + LineNoStr;
601
730
  OS << tag("td", a("#" + TargetName, tag("pre", LineNoStr), TargetName),
602
730
            "line-number");
603
730
}
604
605
void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
606
                                                 CoverageSegmentArray,
607
2
                                                 unsigned) {
608
2
  // Region markers are rendered in-line using tooltips.
609
2
}
610
611
void SourceCoverageViewHTML::renderExpansionSite(
612
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
613
0
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
614
0
  // Render the line containing the expansion site. No extra formatting needed.
615
0
  renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
616
0
}
617
618
void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
619
                                                 ExpansionView &ESV,
620
0
                                                 unsigned ViewDepth) {
621
0
  OS << BeginExpansionDiv;
622
0
  ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
623
0
                  ViewDepth + 1);
624
0
  OS << EndExpansionDiv;
625
0
}
626
627
void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
628
                                                     InstantiationView &ISV,
629
8
                                                     unsigned ViewDepth) {
630
8
  OS << BeginExpansionDiv;
631
8
  if (!ISV.View)
632
2
    OS << BeginSourceNameDiv
633
2
       << tag("pre",
634
2
              escape("Unexecuted instantiation: " + ISV.FunctionName.str(),
635
2
                     getOptions()))
636
2
       << EndSourceNameDiv;
637
8
  else
638
6
    ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
639
6
                    ViewDepth);
640
8
  OS << EndExpansionDiv;
641
8
}
642
643
6
void SourceCoverageViewHTML::renderTitle(raw_ostream &OS, StringRef Title) {
644
6
  if (getOptions().hasProjectTitle())
645
1
    OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions()));
646
6
  OS << tag(ReportTitleTag, escape(Title, getOptions()));
647
6
  if (getOptions().hasCreatedTime())
648
6
    OS << tag(CreatedTimeTag,
649
6
              escape(getOptions().CreatedTimeStr, getOptions()));
650
6
}
651
652
void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS,
653
                                               unsigned FirstUncoveredLineNo,
654
21
                                               unsigned ViewDepth) {
655
21
  std::string SourceLabel;
656
21
  if (
FirstUncoveredLineNo == 021
) {
657
8
    SourceLabel = tag("td", tag("pre", "Source"));
658
21
  } else {
659
13
    std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo));
660
13
    SourceLabel =
661
13
        tag("td", tag("pre", "Source (" +
662
13
                                 a(LinkTarget, "jump to first uncovered line") +
663
13
                                 ")"));
664
13
  }
665
21
666
21
  renderLinePrefix(OS, ViewDepth);
667
21
  OS << tag("td", tag("pre", "Line")) << tag("td", tag("pre", "Count"))
668
21
     << SourceLabel;
669
21
  renderLineSuffix(OS, ViewDepth);
670
21
}