14c01092aSVedant Kumar //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
24c01092aSVedant Kumar //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64c01092aSVedant Kumar //
74c01092aSVedant Kumar //===----------------------------------------------------------------------===//
84c01092aSVedant Kumar ///
94c01092aSVedant Kumar /// \file This file implements the html coverage renderer.
104c01092aSVedant Kumar ///
114c01092aSVedant Kumar //===----------------------------------------------------------------------===//
124c01092aSVedant Kumar
13a59334daSVedant Kumar #include "CoverageReport.h"
144c01092aSVedant Kumar #include "SourceCoverageViewHTML.h"
154c01092aSVedant Kumar #include "llvm/ADT/Optional.h"
164c01092aSVedant Kumar #include "llvm/ADT/SmallString.h"
174c01092aSVedant Kumar #include "llvm/ADT/StringExtras.h"
18a59334daSVedant Kumar #include "llvm/Support/Format.h"
194c01092aSVedant Kumar #include "llvm/Support/Path.h"
204c01092aSVedant Kumar
214c01092aSVedant Kumar using namespace llvm;
224c01092aSVedant Kumar
234c01092aSVedant Kumar namespace {
244c01092aSVedant Kumar
25c076c490SVedant Kumar // Return a string with the special characters in \p Str escaped.
escape(StringRef Str,const CoverageViewOptions & Opts)260ef31b79SYing Yi std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
27e3c1fb8bSVedant Kumar std::string TabExpandedResult;
280ef31b79SYing Yi unsigned ColNum = 0; // Record the column number.
29c076c490SVedant Kumar for (char C : Str) {
30e3c1fb8bSVedant Kumar if (C == '\t') {
31e3c1fb8bSVedant Kumar // Replace '\t' with up to TabSize spaces.
32e3c1fb8bSVedant Kumar unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize);
3352dcbcbfSBenjamin Kramer TabExpandedResult.append(NumSpaces, ' ');
340ef31b79SYing Yi ColNum += NumSpaces;
35e3c1fb8bSVedant Kumar } else {
36e3c1fb8bSVedant Kumar TabExpandedResult += C;
37e3c1fb8bSVedant Kumar if (C == '\n' || C == '\r')
38e3c1fb8bSVedant Kumar ColNum = 0;
39e3c1fb8bSVedant Kumar else
40e3c1fb8bSVedant Kumar ++ColNum;
41c076c490SVedant Kumar }
42e3c1fb8bSVedant Kumar }
43e3c1fb8bSVedant Kumar std::string EscapedHTML;
44e3c1fb8bSVedant Kumar {
45e3c1fb8bSVedant Kumar raw_string_ostream OS{EscapedHTML};
46745918ffSJonas Devlieghere printHTMLEscaped(TabExpandedResult, OS);
47e3c1fb8bSVedant Kumar }
48e3c1fb8bSVedant Kumar return EscapedHTML;
49c076c490SVedant Kumar }
50c076c490SVedant Kumar
51c076c490SVedant Kumar // Create a \p Name tag around \p Str, and optionally set its \p ClassName.
tag(const std::string & Name,const std::string & Str,const std::string & ClassName="")52c076c490SVedant Kumar std::string tag(const std::string &Name, const std::string &Str,
53c076c490SVedant Kumar const std::string &ClassName = "") {
54c076c490SVedant Kumar std::string Tag = "<" + Name;
5516a0de2eSJordan Rupprecht if (!ClassName.empty())
56c076c490SVedant Kumar Tag += " class='" + ClassName + "'";
57c076c490SVedant Kumar return Tag + ">" + Str + "</" + Name + ">";
58c076c490SVedant Kumar }
59c076c490SVedant Kumar
60c076c490SVedant Kumar // Create an anchor to \p Link with the label \p Str.
a(const std::string & Link,const std::string & Str,const std::string & TargetName="")61c076c490SVedant Kumar std::string a(const std::string &Link, const std::string &Str,
625a0e92b0SVedant Kumar const std::string &TargetName = "") {
635a0e92b0SVedant Kumar std::string Name = TargetName.empty() ? "" : ("name='" + TargetName + "' ");
645a0e92b0SVedant Kumar return "<a " + Name + "href='" + Link + "'>" + Str + "</a>";
65c076c490SVedant Kumar }
66c076c490SVedant Kumar
674c01092aSVedant Kumar const char *BeginHeader =
684c01092aSVedant Kumar "<head>"
694c01092aSVedant Kumar "<meta name='viewport' content='width=device-width,initial-scale=1'>"
704c01092aSVedant Kumar "<meta charset='UTF-8'>";
714c01092aSVedant Kumar
724c01092aSVedant Kumar const char *CSSForCoverage =
73c076c490SVedant Kumar R"(.red {
74a59334daSVedant Kumar background-color: #ffd0d0;
754c01092aSVedant Kumar }
764c01092aSVedant Kumar .cyan {
774c01092aSVedant Kumar background-color: cyan;
784c01092aSVedant Kumar }
794c01092aSVedant Kumar body {
804c01092aSVedant Kumar font-family: -apple-system, sans-serif;
814c01092aSVedant Kumar }
824c01092aSVedant Kumar pre {
834c01092aSVedant Kumar margin-top: 0px !important;
844c01092aSVedant Kumar margin-bottom: 0px !important;
854c01092aSVedant Kumar }
864c01092aSVedant Kumar .source-name-title {
874c01092aSVedant Kumar padding: 5px 10px;
884c01092aSVedant Kumar border-bottom: 1px solid #dbdbdb;
894c01092aSVedant Kumar background-color: #eee;
9084dc971eSYing Yi line-height: 35px;
914c01092aSVedant Kumar }
924c01092aSVedant Kumar .centered {
934c01092aSVedant Kumar display: table;
9484dc971eSYing Yi margin-left: left;
954c01092aSVedant Kumar margin-right: auto;
964c01092aSVedant Kumar border: 1px solid #dbdbdb;
974c01092aSVedant Kumar border-radius: 3px;
984c01092aSVedant Kumar }
994c01092aSVedant Kumar .expansion-view {
1004c01092aSVedant Kumar background-color: rgba(0, 0, 0, 0);
1014c01092aSVedant Kumar margin-left: 0px;
1024c01092aSVedant Kumar margin-top: 5px;
1034c01092aSVedant Kumar margin-right: 5px;
1044c01092aSVedant Kumar margin-bottom: 5px;
1054c01092aSVedant Kumar border: 1px solid #dbdbdb;
1064c01092aSVedant Kumar border-radius: 3px;
1074c01092aSVedant Kumar }
1084c01092aSVedant Kumar table {
1094c01092aSVedant Kumar border-collapse: collapse;
1104c01092aSVedant Kumar }
111a59334daSVedant Kumar .light-row {
112a59334daSVedant Kumar background: #ffffff;
113a59334daSVedant Kumar border: 1px solid #dbdbdb;
114a59334daSVedant Kumar }
115790baeedSMax Moroz .light-row-bold {
116790baeedSMax Moroz background: #ffffff;
117790baeedSMax Moroz border: 1px solid #dbdbdb;
118790baeedSMax Moroz font-weight: bold;
119a59334daSVedant Kumar }
120790baeedSMax Moroz .column-entry {
121790baeedSMax Moroz text-align: left;
122790baeedSMax Moroz }
123790baeedSMax Moroz .column-entry-bold {
124790baeedSMax Moroz font-weight: bold;
1257b9e9bb4SVedant Kumar text-align: left;
1267b9e9bb4SVedant Kumar }
127a59334daSVedant Kumar .column-entry-yellow {
128790baeedSMax Moroz text-align: left;
129a59334daSVedant Kumar background-color: #ffffd0;
130a59334daSVedant Kumar }
131790baeedSMax Moroz .column-entry-yellow:hover {
132790baeedSMax Moroz background-color: #fffff0;
133790baeedSMax Moroz }
134a59334daSVedant Kumar .column-entry-red {
135790baeedSMax Moroz text-align: left;
136a59334daSVedant Kumar background-color: #ffd0d0;
137a59334daSVedant Kumar }
138790baeedSMax Moroz .column-entry-red:hover {
139790baeedSMax Moroz background-color: #fff0f0;
140790baeedSMax Moroz }
141a59334daSVedant Kumar .column-entry-green {
142790baeedSMax Moroz text-align: left;
143a59334daSVedant Kumar background-color: #d0ffd0;
144a59334daSVedant Kumar }
145790baeedSMax Moroz .column-entry-green:hover {
146790baeedSMax Moroz background-color: #f0fff0;
147790baeedSMax Moroz }
1484c01092aSVedant Kumar .line-number {
1494c01092aSVedant Kumar text-align: right;
1504c01092aSVedant Kumar color: #aaa;
1514c01092aSVedant Kumar }
1524c01092aSVedant Kumar .covered-line {
1534c01092aSVedant Kumar text-align: right;
1544c01092aSVedant Kumar color: #0080ff;
1554c01092aSVedant Kumar }
1564c01092aSVedant Kumar .uncovered-line {
1574c01092aSVedant Kumar text-align: right;
1584c01092aSVedant Kumar color: #ff3300;
1594c01092aSVedant Kumar }
1604c01092aSVedant Kumar .tooltip {
1614c01092aSVedant Kumar position: relative;
1624c01092aSVedant Kumar display: inline;
1634c01092aSVedant Kumar background-color: #b3e6ff;
1644c01092aSVedant Kumar text-decoration: none;
1654c01092aSVedant Kumar }
1664c01092aSVedant Kumar .tooltip span.tooltip-content {
1674c01092aSVedant Kumar position: absolute;
1684c01092aSVedant Kumar width: 100px;
1694c01092aSVedant Kumar margin-left: -50px;
1704c01092aSVedant Kumar color: #FFFFFF;
1714c01092aSVedant Kumar background: #000000;
1724c01092aSVedant Kumar height: 30px;
1734c01092aSVedant Kumar line-height: 30px;
1744c01092aSVedant Kumar text-align: center;
1754c01092aSVedant Kumar visibility: hidden;
1764c01092aSVedant Kumar border-radius: 6px;
1774c01092aSVedant Kumar }
1784c01092aSVedant Kumar .tooltip span.tooltip-content:after {
1794c01092aSVedant Kumar content: '';
1804c01092aSVedant Kumar position: absolute;
1814c01092aSVedant Kumar top: 100%;
1824c01092aSVedant Kumar left: 50%;
1834c01092aSVedant Kumar margin-left: -8px;
1844c01092aSVedant Kumar width: 0; height: 0;
1854c01092aSVedant Kumar border-top: 8px solid #000000;
1864c01092aSVedant Kumar border-right: 8px solid transparent;
1874c01092aSVedant Kumar border-left: 8px solid transparent;
1884c01092aSVedant Kumar }
1894c01092aSVedant Kumar :hover.tooltip span.tooltip-content {
1904c01092aSVedant Kumar visibility: visible;
1914c01092aSVedant Kumar opacity: 0.8;
1924c01092aSVedant Kumar bottom: 30px;
1934c01092aSVedant Kumar left: 50%;
1944c01092aSVedant Kumar z-index: 999;
1954c01092aSVedant Kumar }
1964c01092aSVedant Kumar th, td {
1974c01092aSVedant Kumar vertical-align: top;
198790baeedSMax Moroz padding: 2px 8px;
1994c01092aSVedant Kumar border-collapse: collapse;
2004c01092aSVedant Kumar border-right: solid 1px #eee;
2014c01092aSVedant Kumar border-left: solid 1px #eee;
202790baeedSMax Moroz text-align: left;
2034c01092aSVedant Kumar }
2045b1083d7SMax Moroz td pre {
2055b1083d7SMax Moroz display: inline-block;
2065b1083d7SMax Moroz }
2074c01092aSVedant Kumar td:first-child {
2084c01092aSVedant Kumar border-left: none;
2094c01092aSVedant Kumar }
2104c01092aSVedant Kumar td:last-child {
2114c01092aSVedant Kumar border-right: none;
2124c01092aSVedant Kumar }
213790baeedSMax Moroz tr:hover {
214790baeedSMax Moroz background-color: #f0f0f0;
215790baeedSMax Moroz }
216c076c490SVedant Kumar )";
2174c01092aSVedant Kumar
2184c01092aSVedant Kumar const char *EndHeader = "</head>";
2194c01092aSVedant Kumar
2204c01092aSVedant Kumar const char *BeginCenteredDiv = "<div class='centered'>";
2214c01092aSVedant Kumar
2224c01092aSVedant Kumar const char *EndCenteredDiv = "</div>";
2234c01092aSVedant Kumar
2244c01092aSVedant Kumar const char *BeginSourceNameDiv = "<div class='source-name-title'>";
2254c01092aSVedant Kumar
2264c01092aSVedant Kumar const char *EndSourceNameDiv = "</div>";
2274c01092aSVedant Kumar
2284c01092aSVedant Kumar const char *BeginCodeTD = "<td class='code'>";
2294c01092aSVedant Kumar
2304c01092aSVedant Kumar const char *EndCodeTD = "</td>";
2314c01092aSVedant Kumar
2324c01092aSVedant Kumar const char *BeginPre = "<pre>";
2334c01092aSVedant Kumar
2344c01092aSVedant Kumar const char *EndPre = "</pre>";
2354c01092aSVedant Kumar
2364c01092aSVedant Kumar const char *BeginExpansionDiv = "<div class='expansion-view'>";
2374c01092aSVedant Kumar
2384c01092aSVedant Kumar const char *EndExpansionDiv = "</div>";
2394c01092aSVedant Kumar
2404c01092aSVedant Kumar const char *BeginTable = "<table>";
2414c01092aSVedant Kumar
2424c01092aSVedant Kumar const char *EndTable = "</table>";
2434c01092aSVedant Kumar
2447b9e9bb4SVedant Kumar const char *ProjectTitleTag = "h1";
24584dc971eSYing Yi
2467b9e9bb4SVedant Kumar const char *ReportTitleTag = "h2";
24784dc971eSYing Yi
2487b9e9bb4SVedant Kumar const char *CreatedTimeTag = "h4";
24984dc971eSYing Yi
getPathToStyle(StringRef ViewPath)250c076c490SVedant Kumar std::string getPathToStyle(StringRef ViewPath) {
25112fc9ca3SKazu Hirata std::string PathToStyle;
252adcd0268SBenjamin Kramer std::string PathSep = std::string(sys::path::get_separator());
253c076c490SVedant Kumar unsigned NumSeps = ViewPath.count(PathSep);
254c076c490SVedant Kumar for (unsigned I = 0, E = NumSeps; I < E; ++I)
255c076c490SVedant Kumar PathToStyle += ".." + PathSep;
256c076c490SVedant Kumar return PathToStyle + "style.css";
257c076c490SVedant Kumar }
258c076c490SVedant Kumar
emitPrelude(raw_ostream & OS,const CoverageViewOptions & Opts,const std::string & PathToStyle="")2590ef31b79SYing Yi void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts,
2600ef31b79SYing Yi const std::string &PathToStyle = "") {
2614c01092aSVedant Kumar OS << "<!doctype html>"
2624c01092aSVedant Kumar "<html>"
263c076c490SVedant Kumar << BeginHeader;
264c076c490SVedant Kumar
265c076c490SVedant Kumar // Link to a stylesheet if one is available. Otherwise, use the default style.
266c076c490SVedant Kumar if (PathToStyle.empty())
267c076c490SVedant Kumar OS << "<style>" << CSSForCoverage << "</style>";
268c076c490SVedant Kumar else
2690ef31b79SYing Yi OS << "<link rel='stylesheet' type='text/css' href='"
2700ef31b79SYing Yi << escape(PathToStyle, Opts) << "'>";
271c076c490SVedant Kumar
27284dc971eSYing Yi OS << EndHeader << "<body>";
2734c01092aSVedant Kumar }
2744c01092aSVedant Kumar
emitEpilog(raw_ostream & OS)2754c01092aSVedant Kumar void emitEpilog(raw_ostream &OS) {
27684dc971eSYing Yi OS << "</body>"
27784dc971eSYing Yi << "</html>";
2784c01092aSVedant Kumar }
2794c01092aSVedant Kumar
2804c01092aSVedant Kumar } // anonymous namespace
2814c01092aSVedant Kumar
2824c01092aSVedant Kumar Expected<CoveragePrinter::OwnedStream>
createViewFile(StringRef Path,bool InToplevel)2834c01092aSVedant Kumar CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
2844c01092aSVedant Kumar auto OSOrErr = createOutputStream(Path, "html", InToplevel);
2854c01092aSVedant Kumar if (!OSOrErr)
2864c01092aSVedant Kumar return OSOrErr;
2874c01092aSVedant Kumar
2884c01092aSVedant Kumar OwnedStream OS = std::move(OSOrErr.get());
289c076c490SVedant Kumar
290c076c490SVedant Kumar if (!Opts.hasOutputDirectory()) {
2910ef31b79SYing Yi emitPrelude(*OS.get(), Opts);
292c076c490SVedant Kumar } else {
293c076c490SVedant Kumar std::string ViewPath = getOutputPath(Path, "html", InToplevel);
2940ef31b79SYing Yi emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath));
295c076c490SVedant Kumar }
296c076c490SVedant Kumar
297c55cf4afSBill Wendling return std::move(OS);
2984c01092aSVedant Kumar }
2994c01092aSVedant Kumar
closeViewFile(OwnedStream OS)3004c01092aSVedant Kumar void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
3014c01092aSVedant Kumar emitEpilog(*OS.get());
3024c01092aSVedant Kumar }
3034c01092aSVedant Kumar
304a59334daSVedant Kumar /// Emit column labels for the table in the index.
emitColumnLabelsForIndex(raw_ostream & OS,const CoverageViewOptions & Opts)30550479f60SEli Friedman static void emitColumnLabelsForIndex(raw_ostream &OS,
30650479f60SEli Friedman const CoverageViewOptions &Opts) {
307a59334daSVedant Kumar SmallVector<std::string, 4> Columns;
308790baeedSMax Moroz Columns.emplace_back(tag("td", "Filename", "column-entry-bold"));
309790baeedSMax Moroz Columns.emplace_back(tag("td", "Function Coverage", "column-entry-bold"));
31050479f60SEli Friedman if (Opts.ShowInstantiationSummary)
311790baeedSMax Moroz Columns.emplace_back(
312790baeedSMax Moroz tag("td", "Instantiation Coverage", "column-entry-bold"));
313790baeedSMax Moroz Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
31450479f60SEli Friedman if (Opts.ShowRegionSummary)
315790baeedSMax Moroz Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
3169f2967bcSAlan Phipps if (Opts.ShowBranchSummary)
3179f2967bcSAlan Phipps Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
318a59334daSVedant Kumar OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
319a59334daSVedant Kumar }
320a59334daSVedant Kumar
321c0c182ccSEli Friedman std::string
buildLinkToFile(StringRef SF,const FileCoverageSummary & FCS) const322c0c182ccSEli Friedman CoveragePrinterHTML::buildLinkToFile(StringRef SF,
323c0c182ccSEli Friedman const FileCoverageSummary &FCS) const {
324c0c182ccSEli Friedman SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
325435a5a36SKazu Hirata sys::path::remove_dots(LinkTextStr, /*remove_dot_dot=*/true);
326c0c182ccSEli Friedman sys::path::native(LinkTextStr);
327c0c182ccSEli Friedman std::string LinkText = escape(LinkTextStr, Opts);
328c0c182ccSEli Friedman std::string LinkTarget =
329c0c182ccSEli Friedman escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
330c0c182ccSEli Friedman return a(LinkTarget, LinkText);
331c0c182ccSEli Friedman }
332c0c182ccSEli Friedman
333a59334daSVedant Kumar /// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is
334a59334daSVedant Kumar /// false, link the summary to \p SF.
emitFileSummary(raw_ostream & OS,StringRef SF,const FileCoverageSummary & FCS,bool IsTotals) const335a59334daSVedant Kumar void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
336a59334daSVedant Kumar const FileCoverageSummary &FCS,
337a59334daSVedant Kumar bool IsTotals) const {
338224ef8d7SVedant Kumar SmallVector<std::string, 8> Columns;
339a59334daSVedant Kumar
340a59334daSVedant Kumar // Format a coverage triple and add the result to the list of columns.
341b5f1a8cfSPetr Hosek auto AddCoverageTripleToColumn =
342b5f1a8cfSPetr Hosek [&Columns, this](unsigned Hit, unsigned Total, float Pctg) {
343a59334daSVedant Kumar std::string S;
344a59334daSVedant Kumar {
345a59334daSVedant Kumar raw_string_ostream RSO{S};
34635369c1eSAlex Lorenz if (Total)
34774295975SMax Moroz RSO << format("%*.2f", 7, Pctg) << "% ";
34835369c1eSAlex Lorenz else
34935369c1eSAlex Lorenz RSO << "- ";
35035369c1eSAlex Lorenz RSO << '(' << Hit << '/' << Total << ')';
351a59334daSVedant Kumar }
352a59334daSVedant Kumar const char *CellClass = "column-entry-yellow";
353b5f1a8cfSPetr Hosek if (Pctg >= Opts.HighCovWatermark)
354a59334daSVedant Kumar CellClass = "column-entry-green";
355b5f1a8cfSPetr Hosek else if (Pctg < Opts.LowCovWatermark)
35635369c1eSAlex Lorenz CellClass = "column-entry-red";
3577b9e9bb4SVedant Kumar Columns.emplace_back(tag("td", tag("pre", S), CellClass));
358a59334daSVedant Kumar };
359a59334daSVedant Kumar
360a59334daSVedant Kumar // Simplify the display file path, and wrap it in a link if requested.
361a59334daSVedant Kumar std::string Filename;
362224ef8d7SVedant Kumar if (IsTotals) {
363adcd0268SBenjamin Kramer Filename = std::string(SF);
364224ef8d7SVedant Kumar } else {
365c0c182ccSEli Friedman Filename = buildLinkToFile(SF, FCS);
366a59334daSVedant Kumar }
367a59334daSVedant Kumar
3687b9e9bb4SVedant Kumar Columns.emplace_back(tag("td", tag("pre", Filename)));
369c445e65dSVedant Kumar AddCoverageTripleToColumn(FCS.FunctionCoverage.getExecuted(),
370c445e65dSVedant Kumar FCS.FunctionCoverage.getNumFunctions(),
371a59334daSVedant Kumar FCS.FunctionCoverage.getPercentCovered());
37250479f60SEli Friedman if (Opts.ShowInstantiationSummary)
373c445e65dSVedant Kumar AddCoverageTripleToColumn(FCS.InstantiationCoverage.getExecuted(),
374c445e65dSVedant Kumar FCS.InstantiationCoverage.getNumFunctions(),
375016111f7SVedant Kumar FCS.InstantiationCoverage.getPercentCovered());
376c445e65dSVedant Kumar AddCoverageTripleToColumn(FCS.LineCoverage.getCovered(),
377c445e65dSVedant Kumar FCS.LineCoverage.getNumLines(),
378673ad727SVedant Kumar FCS.LineCoverage.getPercentCovered());
37950479f60SEli Friedman if (Opts.ShowRegionSummary)
380c445e65dSVedant Kumar AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
381c445e65dSVedant Kumar FCS.RegionCoverage.getNumRegions(),
382673ad727SVedant Kumar FCS.RegionCoverage.getPercentCovered());
3839f2967bcSAlan Phipps if (Opts.ShowBranchSummary)
3849f2967bcSAlan Phipps AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
3859f2967bcSAlan Phipps FCS.BranchCoverage.getNumBranches(),
3869f2967bcSAlan Phipps FCS.BranchCoverage.getPercentCovered());
387a59334daSVedant Kumar
388790baeedSMax Moroz if (IsTotals)
389790baeedSMax Moroz OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
390790baeedSMax Moroz else
391a59334daSVedant Kumar OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
392a59334daSVedant Kumar }
393a59334daSVedant Kumar
createIndexFile(ArrayRef<std::string> SourceFiles,const CoverageMapping & Coverage,const CoverageFiltersMatchAll & Filters)394a59334daSVedant Kumar Error CoveragePrinterHTML::createIndexFile(
395e955f618SVedant Kumar ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
396d932b2d7SSean Eveson const CoverageFiltersMatchAll &Filters) {
397a59334daSVedant Kumar // Emit the default stylesheet.
398a59334daSVedant Kumar auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
399a59334daSVedant Kumar if (Error E = CSSOrErr.takeError())
400a59334daSVedant Kumar return E;
401a59334daSVedant Kumar
402a59334daSVedant Kumar OwnedStream CSS = std::move(CSSOrErr.get());
403a59334daSVedant Kumar CSS->operator<<(CSSForCoverage);
404a59334daSVedant Kumar
405a59334daSVedant Kumar // Emit a file index along with some coverage statistics.
4064c01092aSVedant Kumar auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
4074c01092aSVedant Kumar if (Error E = OSOrErr.takeError())
4084c01092aSVedant Kumar return E;
4094c01092aSVedant Kumar auto OS = std::move(OSOrErr.get());
4104c01092aSVedant Kumar raw_ostream &OSRef = *OS.get();
4114c01092aSVedant Kumar
412127d0502SVedant Kumar assert(Opts.hasOutputDirectory() && "No output directory for index file");
4130ef31b79SYing Yi emitPrelude(OSRef, Opts, getPathToStyle(""));
414a59334daSVedant Kumar
415a59334daSVedant Kumar // Emit some basic information about the coverage report.
41684dc971eSYing Yi if (Opts.hasProjectTitle())
4177b9e9bb4SVedant Kumar OSRef << tag(ProjectTitleTag, escape(Opts.ProjectTitle, Opts));
4187b9e9bb4SVedant Kumar OSRef << tag(ReportTitleTag, "Coverage Report");
41984dc971eSYing Yi if (Opts.hasCreatedTime())
4207b9e9bb4SVedant Kumar OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts));
421a59334daSVedant Kumar
42291743f28SVedant Kumar // Emit a link to some documentation.
42391743f28SVedant Kumar OSRef << tag("p", "Click " +
42491743f28SVedant Kumar a("http://clang.llvm.org/docs/"
42591743f28SVedant Kumar "SourceBasedCodeCoverage.html#interpreting-reports",
42691743f28SVedant Kumar "here") +
42791743f28SVedant Kumar " for information about interpreting this report.");
42891743f28SVedant Kumar
429a59334daSVedant Kumar // Emit a table containing links to reports for each file in the covmapping.
430c0c182ccSEli Friedman // Exclude files which don't contain any regions.
43184dc971eSYing Yi OSRef << BeginCenteredDiv << BeginTable;
43250479f60SEli Friedman emitColumnLabelsForIndex(OSRef, Opts);
433a59334daSVedant Kumar FileCoverageSummary Totals("TOTALS");
434fa8ef35eSSean Eveson auto FileReports = CoverageReport::prepareFileReports(
435fa8ef35eSSean Eveson Coverage, Totals, SourceFiles, Opts, Filters);
436c0c182ccSEli Friedman bool EmptyFiles = false;
437c0c182ccSEli Friedman for (unsigned I = 0, E = FileReports.size(); I < E; ++I) {
438c445e65dSVedant Kumar if (FileReports[I].FunctionCoverage.getNumFunctions())
439a59334daSVedant Kumar emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
440c0c182ccSEli Friedman else
441c0c182ccSEli Friedman EmptyFiles = true;
442c0c182ccSEli Friedman }
443a59334daSVedant Kumar emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
444c0c182ccSEli Friedman OSRef << EndTable << EndCenteredDiv;
445c0c182ccSEli Friedman
446c0c182ccSEli Friedman // Emit links to files which don't contain any functions. These are normally
447c0c182ccSEli Friedman // not very useful, but could be relevant for code which abuses the
448c0c182ccSEli Friedman // preprocessor.
449d932b2d7SSean Eveson if (EmptyFiles && Filters.empty()) {
450c0c182ccSEli Friedman OSRef << tag("p", "Files which contain no functions. (These "
451c0c182ccSEli Friedman "files contain code pulled into other files "
452c0c182ccSEli Friedman "by the preprocessor.)\n");
453c0c182ccSEli Friedman OSRef << BeginCenteredDiv << BeginTable;
454c0c182ccSEli Friedman for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
455c445e65dSVedant Kumar if (!FileReports[I].FunctionCoverage.getNumFunctions()) {
456c0c182ccSEli Friedman std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]);
457c0c182ccSEli Friedman OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n';
458c0c182ccSEli Friedman }
459c0c182ccSEli Friedman OSRef << EndTable << EndCenteredDiv;
460c0c182ccSEli Friedman }
461c0c182ccSEli Friedman
462c0c182ccSEli Friedman OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts));
4634c01092aSVedant Kumar emitEpilog(OSRef);
4644c01092aSVedant Kumar
4654c01092aSVedant Kumar return Error::success();
4664c01092aSVedant Kumar }
4674c01092aSVedant Kumar
renderViewHeader(raw_ostream & OS)4684c01092aSVedant Kumar void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
4697b9e9bb4SVedant Kumar OS << BeginCenteredDiv << BeginTable;
4704c01092aSVedant Kumar }
4714c01092aSVedant Kumar
renderViewFooter(raw_ostream & OS)4724c01092aSVedant Kumar void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
47384a280adSVedant Kumar OS << EndTable << EndCenteredDiv;
4744c01092aSVedant Kumar }
4754c01092aSVedant Kumar
renderSourceName(raw_ostream & OS,bool WholeFile)476b1c174aaSVedant Kumar void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) {
4775c61c703SVedant Kumar OS << BeginSourceNameDiv << tag("pre", escape(getSourceName(), getOptions()))
4785c61c703SVedant Kumar << EndSourceNameDiv;
4794c01092aSVedant Kumar }
4804c01092aSVedant Kumar
renderLinePrefix(raw_ostream & OS,unsigned)4814c01092aSVedant Kumar void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
4824c01092aSVedant Kumar OS << "<tr>";
4834c01092aSVedant Kumar }
4844c01092aSVedant Kumar
renderLineSuffix(raw_ostream & OS,unsigned)4854c01092aSVedant Kumar void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
4864c01092aSVedant Kumar // If this view has sub-views, renderLine() cannot close the view's cell.
4874c01092aSVedant Kumar // Take care of it here, after all sub-views have been rendered.
4884c01092aSVedant Kumar if (hasSubViews())
4894c01092aSVedant Kumar OS << EndCodeTD;
4904c01092aSVedant Kumar OS << "</tr>";
4914c01092aSVedant Kumar }
4924c01092aSVedant Kumar
renderViewDivider(raw_ostream &,unsigned)4934c01092aSVedant Kumar void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
4944c01092aSVedant Kumar // The table-based output makes view dividers unnecessary.
4954c01092aSVedant Kumar }
4964c01092aSVedant Kumar
renderLine(raw_ostream & OS,LineRef L,const LineCoverageStats & LCS,unsigned ExpansionCol,unsigned)49708a0a310SVedant Kumar void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L,
49808a0a310SVedant Kumar const LineCoverageStats &LCS,
49908a0a310SVedant Kumar unsigned ExpansionCol, unsigned) {
5004c01092aSVedant Kumar StringRef Line = L.Line;
501fc07e8b4SVedant Kumar unsigned LineNo = L.LineNo;
5024c01092aSVedant Kumar
5034c01092aSVedant Kumar // Steps for handling text-escaping, highlighting, and tooltip creation:
5044c01092aSVedant Kumar //
5054c01092aSVedant Kumar // 1. Split the line into N+1 snippets, where N = |Segments|. The first
5064c01092aSVedant Kumar // snippet starts from Col=1 and ends at the start of the first segment.
5074c01092aSVedant Kumar // The last snippet starts at the last mapped column in the line and ends
5084c01092aSVedant Kumar // at the end of the line. Both are required but may be empty.
5094c01092aSVedant Kumar
5104c01092aSVedant Kumar SmallVector<std::string, 8> Snippets;
51108a0a310SVedant Kumar CoverageSegmentArray Segments = LCS.getLineSegments();
5124c01092aSVedant Kumar
5134c01092aSVedant Kumar unsigned LCol = 1;
5144c01092aSVedant Kumar auto Snip = [&](unsigned Start, unsigned Len) {
515adcd0268SBenjamin Kramer Snippets.push_back(std::string(Line.substr(Start, Len)));
5164c01092aSVedant Kumar LCol += Len;
5174c01092aSVedant Kumar };
5184c01092aSVedant Kumar
5194c01092aSVedant Kumar Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));
5204c01092aSVedant Kumar
521c236e5a0SVedant Kumar for (unsigned I = 1, E = Segments.size(); I < E; ++I)
5224c01092aSVedant Kumar Snip(LCol - 1, Segments[I]->Col - LCol);
5234c01092aSVedant Kumar
5244c01092aSVedant Kumar // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
5254c01092aSVedant Kumar Snip(LCol - 1, Line.size() + 1 - LCol);
5264c01092aSVedant Kumar
5274c01092aSVedant Kumar // 2. Escape all of the snippets.
5284c01092aSVedant Kumar
5294c01092aSVedant Kumar for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
5300ef31b79SYing Yi Snippets[I] = escape(Snippets[I], getOptions());
5314c01092aSVedant Kumar
532fc07e8b4SVedant Kumar // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment
533fc07e8b4SVedant Kumar // 1 to set the highlight for snippet 2, segment 2 to set the highlight for
534fc07e8b4SVedant Kumar // snippet 3, and so on.
5354c01092aSVedant Kumar
536988faf87SVedant Kumar Optional<StringRef> Color;
5370b33f2c0SVedant Kumar SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
538fc07e8b4SVedant Kumar auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) {
5390b33f2c0SVedant Kumar if (getOptions().Debug)
540fc07e8b4SVedant Kumar HighlightedRanges.emplace_back(LC, RC);
5417a47ee51SKazu Hirata return tag("span", Snippet, std::string(*Color));
5424c01092aSVedant Kumar };
5434c01092aSVedant Kumar
54443247f05SVedant Kumar auto CheckIfUncovered = [&](const CoverageSegment *S) {
54543247f05SVedant Kumar return S && (!S->IsGapRegion || (Color && *Color == "red")) &&
54643247f05SVedant Kumar S->HasCount && S->Count == 0;
5474c01092aSVedant Kumar };
5484c01092aSVedant Kumar
54908a0a310SVedant Kumar if (CheckIfUncovered(LCS.getWrappedSegment())) {
5504c01092aSVedant Kumar Color = "red";
5510b33f2c0SVedant Kumar if (!Snippets[0].empty())
5520b33f2c0SVedant Kumar Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size());
5534c01092aSVedant Kumar }
5544c01092aSVedant Kumar
555fc07e8b4SVedant Kumar for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
5564c01092aSVedant Kumar const auto *CurSeg = Segments[I];
55743247f05SVedant Kumar if (CheckIfUncovered(CurSeg))
5584c01092aSVedant Kumar Color = "red";
55943247f05SVedant Kumar else if (CurSeg->Col == ExpansionCol)
56043247f05SVedant Kumar Color = "cyan";
5614c01092aSVedant Kumar else
5624c01092aSVedant Kumar Color = None;
5634c01092aSVedant Kumar
564*a7938c74SKazu Hirata if (Color)
565fc07e8b4SVedant Kumar Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col,
566fc07e8b4SVedant Kumar CurSeg->Col + Snippets[I + 1].size());
567fc07e8b4SVedant Kumar }
568fc07e8b4SVedant Kumar
569*a7938c74SKazu Hirata if (Color && Segments.empty())
5700b33f2c0SVedant Kumar Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size());
571fc07e8b4SVedant Kumar
572fc07e8b4SVedant Kumar if (getOptions().Debug) {
573fc07e8b4SVedant Kumar for (const auto &Range : HighlightedRanges) {
5740b33f2c0SVedant Kumar errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> ";
575fc07e8b4SVedant Kumar if (Range.second == 0)
576fc07e8b4SVedant Kumar errs() << "?";
577fc07e8b4SVedant Kumar else
5780b33f2c0SVedant Kumar errs() << Range.second;
579fc07e8b4SVedant Kumar errs() << "\n";
580fc07e8b4SVedant Kumar }
5814c01092aSVedant Kumar }
5824c01092aSVedant Kumar
5834c01092aSVedant Kumar // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
5844c01092aSVedant Kumar // sub-line region count tooltips if needed.
5854c01092aSVedant Kumar
58669597042SVedant Kumar if (shouldRenderRegionMarkers(LCS)) {
587933b37f9SVedant Kumar // Just consider the segments which start *and* end on this line.
588933b37f9SVedant Kumar for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {
5894c01092aSVedant Kumar const auto *CurSeg = Segments[I];
590933b37f9SVedant Kumar if (!CurSeg->IsRegionEntry)
5914c01092aSVedant Kumar continue;
5929cbd33feSVedant Kumar if (CurSeg->Count == LCS.getExecutionCount())
5939cbd33feSVedant Kumar continue;
5944c01092aSVedant Kumar
5954c01092aSVedant Kumar Snippets[I + 1] =
5964c01092aSVedant Kumar tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
5974c01092aSVedant Kumar "tooltip-content"),
5984c01092aSVedant Kumar "tooltip");
599933b37f9SVedant Kumar
600933b37f9SVedant Kumar if (getOptions().Debug)
601933b37f9SVedant Kumar errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = "
602933b37f9SVedant Kumar << formatCount(CurSeg->Count) << "\n";
6034c01092aSVedant Kumar }
6044c01092aSVedant Kumar }
6054c01092aSVedant Kumar
6064c01092aSVedant Kumar OS << BeginCodeTD;
6074c01092aSVedant Kumar OS << BeginPre;
6084c01092aSVedant Kumar for (const auto &Snippet : Snippets)
6094c01092aSVedant Kumar OS << Snippet;
6104c01092aSVedant Kumar OS << EndPre;
6114c01092aSVedant Kumar
6124c01092aSVedant Kumar // If there are no sub-views left to attach to this cell, end the cell.
6134c01092aSVedant Kumar // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
6144c01092aSVedant Kumar if (!hasSubViews())
6154c01092aSVedant Kumar OS << EndCodeTD;
6164c01092aSVedant Kumar }
6174c01092aSVedant Kumar
renderLineCoverageColumn(raw_ostream & OS,const LineCoverageStats & Line)6184c01092aSVedant Kumar void SourceCoverageViewHTML::renderLineCoverageColumn(
6194c01092aSVedant Kumar raw_ostream &OS, const LineCoverageStats &Line) {
62012fc9ca3SKazu Hirata std::string Count;
6214c01092aSVedant Kumar if (Line.isMapped())
6221963f51fSVedant Kumar Count = tag("pre", formatCount(Line.getExecutionCount()));
6234c01092aSVedant Kumar std::string CoverageClass =
6241963f51fSVedant Kumar (Line.getExecutionCount() > 0) ? "covered-line" : "uncovered-line";
6254c01092aSVedant Kumar OS << tag("td", Count, CoverageClass);
6264c01092aSVedant Kumar }
6274c01092aSVedant Kumar
renderLineNumberColumn(raw_ostream & OS,unsigned LineNo)6284c01092aSVedant Kumar void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
6294c01092aSVedant Kumar unsigned LineNo) {
6302e089362SVedant Kumar std::string LineNoStr = utostr(uint64_t(LineNo));
6315a0e92b0SVedant Kumar std::string TargetName = "L" + LineNoStr;
6325a0e92b0SVedant Kumar OS << tag("td", a("#" + TargetName, tag("pre", LineNoStr), TargetName),
6332e089362SVedant Kumar "line-number");
6344c01092aSVedant Kumar }
6354c01092aSVedant Kumar
renderRegionMarkers(raw_ostream &,const LineCoverageStats & Line,unsigned)6364c01092aSVedant Kumar void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
63708a0a310SVedant Kumar const LineCoverageStats &Line,
6384c01092aSVedant Kumar unsigned) {
6394c01092aSVedant Kumar // Region markers are rendered in-line using tooltips.
6404c01092aSVedant Kumar }
6414c01092aSVedant Kumar
renderExpansionSite(raw_ostream & OS,LineRef L,const LineCoverageStats & LCS,unsigned ExpansionCol,unsigned ViewDepth)64208a0a310SVedant Kumar void SourceCoverageViewHTML::renderExpansionSite(raw_ostream &OS, LineRef L,
64308a0a310SVedant Kumar const LineCoverageStats &LCS,
64408a0a310SVedant Kumar unsigned ExpansionCol,
64508a0a310SVedant Kumar unsigned ViewDepth) {
6464c01092aSVedant Kumar // Render the line containing the expansion site. No extra formatting needed.
64708a0a310SVedant Kumar renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
6484c01092aSVedant Kumar }
6494c01092aSVedant Kumar
renderExpansionView(raw_ostream & OS,ExpansionView & ESV,unsigned ViewDepth)6504c01092aSVedant Kumar void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
6514c01092aSVedant Kumar ExpansionView &ESV,
6524c01092aSVedant Kumar unsigned ViewDepth) {
6534c01092aSVedant Kumar OS << BeginExpansionDiv;
6544c01092aSVedant Kumar ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
655fa8ef35eSSean Eveson /*ShowTitle=*/false, ViewDepth + 1);
6564c01092aSVedant Kumar OS << EndExpansionDiv;
6574c01092aSVedant Kumar }
6584c01092aSVedant Kumar
renderBranchView(raw_ostream & OS,BranchView & BRV,unsigned ViewDepth)6599f2967bcSAlan Phipps void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
6609f2967bcSAlan Phipps unsigned ViewDepth) {
6619f2967bcSAlan Phipps // Render the child subview.
6629f2967bcSAlan Phipps if (getOptions().Debug)
6639f2967bcSAlan Phipps errs() << "Branch at line " << BRV.getLine() << '\n';
6649f2967bcSAlan Phipps
6659f2967bcSAlan Phipps OS << BeginExpansionDiv;
6669f2967bcSAlan Phipps OS << BeginPre;
6679f2967bcSAlan Phipps for (const auto &R : BRV.Regions) {
6689f2967bcSAlan Phipps // Calculate TruePercent and False Percent.
6699f2967bcSAlan Phipps double TruePercent = 0.0;
6709f2967bcSAlan Phipps double FalsePercent = 0.0;
6719f2967bcSAlan Phipps unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
6729f2967bcSAlan Phipps
6739f2967bcSAlan Phipps if (!getOptions().ShowBranchCounts && Total != 0) {
6749f2967bcSAlan Phipps TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
6759f2967bcSAlan Phipps FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
6769f2967bcSAlan Phipps }
6779f2967bcSAlan Phipps
6789f2967bcSAlan Phipps // Display Line + Column.
6799f2967bcSAlan Phipps std::string LineNoStr = utostr(uint64_t(R.LineStart));
6809f2967bcSAlan Phipps std::string ColNoStr = utostr(uint64_t(R.ColumnStart));
6819f2967bcSAlan Phipps std::string TargetName = "L" + LineNoStr;
6829f2967bcSAlan Phipps
6839f2967bcSAlan Phipps OS << " Branch (";
6849f2967bcSAlan Phipps OS << tag("span",
6859f2967bcSAlan Phipps a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
6869f2967bcSAlan Phipps TargetName),
6879f2967bcSAlan Phipps "line-number") +
6889f2967bcSAlan Phipps "): [";
6899f2967bcSAlan Phipps
6909f2967bcSAlan Phipps if (R.Folded) {
6919f2967bcSAlan Phipps OS << "Folded - Ignored]\n";
6929f2967bcSAlan Phipps continue;
6939f2967bcSAlan Phipps }
6949f2967bcSAlan Phipps
6959f2967bcSAlan Phipps // Display TrueCount or TruePercent.
6969f2967bcSAlan Phipps std::string TrueColor = R.ExecutionCount ? "None" : "red";
6979f2967bcSAlan Phipps std::string TrueCovClass =
6989f2967bcSAlan Phipps (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
6999f2967bcSAlan Phipps
7009f2967bcSAlan Phipps OS << tag("span", "True", TrueColor);
7019f2967bcSAlan Phipps OS << ": ";
7029f2967bcSAlan Phipps if (getOptions().ShowBranchCounts)
7039f2967bcSAlan Phipps OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", ";
7049f2967bcSAlan Phipps else
7059f2967bcSAlan Phipps OS << format("%0.2f", TruePercent) << "%, ";
7069f2967bcSAlan Phipps
7079f2967bcSAlan Phipps // Display FalseCount or FalsePercent.
7089f2967bcSAlan Phipps std::string FalseColor = R.FalseExecutionCount ? "None" : "red";
7099f2967bcSAlan Phipps std::string FalseCovClass =
7109f2967bcSAlan Phipps (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line";
7119f2967bcSAlan Phipps
7129f2967bcSAlan Phipps OS << tag("span", "False", FalseColor);
7139f2967bcSAlan Phipps OS << ": ";
7149f2967bcSAlan Phipps if (getOptions().ShowBranchCounts)
7159f2967bcSAlan Phipps OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass);
7169f2967bcSAlan Phipps else
7179f2967bcSAlan Phipps OS << format("%0.2f", FalsePercent) << "%";
7189f2967bcSAlan Phipps
7199f2967bcSAlan Phipps OS << "]\n";
7209f2967bcSAlan Phipps }
7219f2967bcSAlan Phipps OS << EndPre;
7229f2967bcSAlan Phipps OS << EndExpansionDiv;
7239f2967bcSAlan Phipps }
7249f2967bcSAlan Phipps
renderInstantiationView(raw_ostream & OS,InstantiationView & ISV,unsigned ViewDepth)7254c01092aSVedant Kumar void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
7264c01092aSVedant Kumar InstantiationView &ISV,
7274c01092aSVedant Kumar unsigned ViewDepth) {
7284c01092aSVedant Kumar OS << BeginExpansionDiv;
729a8c396d9SVedant Kumar if (!ISV.View)
730a8c396d9SVedant Kumar OS << BeginSourceNameDiv
731a8c396d9SVedant Kumar << tag("pre",
732a8c396d9SVedant Kumar escape("Unexecuted instantiation: " + ISV.FunctionName.str(),
733a8c396d9SVedant Kumar getOptions()))
734a8c396d9SVedant Kumar << EndSourceNameDiv;
735a8c396d9SVedant Kumar else
736a8c396d9SVedant Kumar ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
737fa8ef35eSSean Eveson /*ShowTitle=*/false, ViewDepth);
7384c01092aSVedant Kumar OS << EndExpansionDiv;
7394c01092aSVedant Kumar }
74084dc971eSYing Yi
renderTitle(raw_ostream & OS,StringRef Title)741b2edd11fSVedant Kumar void SourceCoverageViewHTML::renderTitle(raw_ostream &OS, StringRef Title) {
74284dc971eSYing Yi if (getOptions().hasProjectTitle())
7437b9e9bb4SVedant Kumar OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions()));
744b2edd11fSVedant Kumar OS << tag(ReportTitleTag, escape(Title, getOptions()));
74584dc971eSYing Yi if (getOptions().hasCreatedTime())
7467b9e9bb4SVedant Kumar OS << tag(CreatedTimeTag,
7477b9e9bb4SVedant Kumar escape(getOptions().CreatedTimeStr, getOptions()));
74884dc971eSYing Yi }
74984dc971eSYing Yi
renderTableHeader(raw_ostream & OS,unsigned FirstUncoveredLineNo,unsigned ViewDepth)75084dc971eSYing Yi void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS,
751b1c174aaSVedant Kumar unsigned FirstUncoveredLineNo,
75284dc971eSYing Yi unsigned ViewDepth) {
753b1c174aaSVedant Kumar std::string SourceLabel;
754408866caSVedant Kumar if (FirstUncoveredLineNo == 0) {
755b1c174aaSVedant Kumar SourceLabel = tag("td", tag("pre", "Source"));
756b1c174aaSVedant Kumar } else {
757b1c174aaSVedant Kumar std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo));
758b1c174aaSVedant Kumar SourceLabel =
759b1c174aaSVedant Kumar tag("td", tag("pre", "Source (" +
760b1c174aaSVedant Kumar a(LinkTarget, "jump to first uncovered line") +
761b1c174aaSVedant Kumar ")"));
762b1c174aaSVedant Kumar }
763b1c174aaSVedant Kumar
76484dc971eSYing Yi renderLinePrefix(OS, ViewDepth);
76598ba34e5SVedant Kumar OS << tag("td", tag("pre", "Line")) << tag("td", tag("pre", "Count"))
766b1c174aaSVedant Kumar << SourceLabel;
76784dc971eSYing Yi renderLineSuffix(OS, ViewDepth);
76884dc971eSYing Yi }
769