[PATCH][RFC] llvm-cov HTML generation
Justin Bogner via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 7 09:02:47 PST 2016
Harlan Haskins <hhaskins at apple.com> writes:
> Hi all,
>
> Thanks again for the reviews!
>
> I’ve restructured how I handle subviews and pulled out the common behavior. I
> also addressed the issue with highlighting and showing macro expansions (and
> found a possible bug in clang because of it).
>
> Attached is a) a new patch, and b) an HTML file showing a single line
> multi-macro expansion.
This is coming along, thanks!
I wonder if the rendering logic would be easier to follow if you
structured it as a concrete/final SourceCoverageView class that "has-a"
TextRenderer/HTMLRenderer. I suspect that most of the rendering state
could be hidden inside the renderer type and separated a bit from the
logic for walking the coverage. Thoughts?
> Thanks,
> Harlan Haskins
>
>
> Index: tools/llvm-cov/CodeCoverage.cpp
> ===================================================================
> --- tools/llvm-cov/CodeCoverage.cpp (revision 262364)
> +++ tools/llvm-cov/CodeCoverage.cpp (working copy)
> @@ -51,6 +51,10 @@
> /// \brief Print the error message to the error output stream.
> void error(const Twine &Message, StringRef Whence = "");
>
> + /// \brief Return a pointer to a stream for the given file.
> + /// Passing "" yields stdout.
> + std::unique_ptr<raw_ostream> getStreamForFile(std::string Filename);
> +
> /// \brief Return a memory buffer for the given source file.
> ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
>
> @@ -100,6 +104,22 @@
> errs() << Message << "\n";
> }
>
> +std::unique_ptr<raw_ostream>
> +CodeCoverageTool::getStreamForFile(std::string Filename) {
> + if (Filename == "") {
> + return make_unique<raw_fd_ostream>(fileno(stdout), false);
> + }
> +
> + std::error_code Error;
> + auto OS = make_unique<raw_fd_ostream>(Filename, Error, sys::fs::F_RW);
> + if (Error) {
> + error(Error.message(), "getting stream for " + Filename);
> + return nullptr;
> + }
> +
> + return std::move(OS);
> +}
> +
> ErrorOr<const MemoryBuffer &>
> CodeCoverageTool::getSourceFile(StringRef SourceFile) {
> // If we've remapped filenames, look up the real location for this file.
> @@ -135,8 +155,9 @@
> continue;
>
> auto SubViewExpansions = ExpansionCoverage.getExpansions();
> - auto SubView = llvm::make_unique<SourceCoverageView>(
> - SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
> + auto SubView = SourceCoverageView::create(SourceBuffer.get(), ViewOpts,
> + Expansion.Function.Name,
> + std::move(ExpansionCoverage));
> attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
> View.addExpansion(Expansion.Region, std::move(SubView));
> }
> @@ -153,8 +174,8 @@
> return nullptr;
>
> auto Expansions = FunctionCoverage.getExpansions();
> - auto View = llvm::make_unique<SourceCoverageView>(
> - SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
> + auto View = SourceCoverageView::create(
> + SourceBuffer.get(), ViewOpts, Function.Name, std::move(FunctionCoverage));
> attachExpansionSubViews(*View, Expansions, Coverage);
>
> return View;
> @@ -171,15 +192,16 @@
> return nullptr;
>
> auto Expansions = FileCoverage.getExpansions();
> - auto View = llvm::make_unique<SourceCoverageView>(
> - SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
> + auto View = SourceCoverageView::create(SourceBuffer.get(), ViewOpts,
> + SourceFile, std::move(FileCoverage));
> attachExpansionSubViews(*View, Expansions, Coverage);
>
> for (auto Function : Coverage.getInstantiations(SourceFile)) {
> auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
> auto SubViewExpansions = SubViewCoverage.getExpansions();
> - auto SubView = llvm::make_unique<SourceCoverageView>(
> - SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
> + auto SubView =
> + SourceCoverageView::create(SourceBuffer.get(), ViewOpts, Function->Name,
> + std::move(SubViewCoverage));
> attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
>
> if (SubView) {
> @@ -315,9 +337,16 @@
> ViewOpts.Debug = DebugDump;
> CompareFilenamesOnly = FilenameEquivalence;
>
> - ViewOpts.Colors = UseColor == cl::BOU_UNSET
> - ? sys::Process::StandardOutHasColors()
> - : UseColor == cl::BOU_TRUE;
> + switch (ViewOpts.Format) {
> + case CoverageViewOptions::OFConsole:
> + ViewOpts.Colors = UseColor == cl::BOU_UNSET
> + ? sys::Process::StandardOutHasColors()
> + : UseColor == cl::BOU_TRUE;
> + break;
> + case CoverageViewOptions::OFHTML:
> + ViewOpts.Colors = true;
> + break;
> + }
>
> // Create the function filters
> if (!NameFilters.empty() || !NameRegexFilters.empty()) {
> @@ -406,6 +435,18 @@
> cl::desc("Show function instantiations"),
> cl::cat(ViewCategory));
>
> + cl::opt<std::string> OutputDirectory(
> + "o", "output-dir", cl::Optional,
> + cl::desc("Directory to output individual files"));
> +
> + cl::opt<CoverageViewOptions::OutputFormat> Format(
> + "f", "format", cl::desc("Format to output comparison"),
> + cl::values(clEnumValN(CoverageViewOptions::OFHTML, "html", "HTML output"),
> + clEnumValN(CoverageViewOptions::OFConsole, "console",
> + "Console table output"),
> + clEnumValEnd),
> + cl::init(CoverageViewOptions::OFConsole));
> +
> auto Err = commandLineParser(argc, argv);
> if (Err)
> return Err;
> @@ -417,12 +458,29 @@
> ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
> ViewOpts.ShowExpandedRegions = ShowExpansions;
> ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
> + ViewOpts.Format = Format;
>
> auto Coverage = load();
> if (!Coverage)
> return 1;
>
> + bool isHTML = Format == CoverageViewOptions::OFHTML;
> + std::string FileExt = isHTML ? "html" : "txt";
> +
> if (!Filters.empty()) {
> + std::string OutputPath = OutputDirectory;
> + if (OutputPath != "") {
> + sys::fs::create_directories(OutputDirectory);
> + OutputPath += "/functions." + FileExt;
> + }
> + auto OS = getStreamForFile(OutputPath);
> + if (!OS)
> + return 1;
> +
> + if (isHTML) {
> + SourceCoverageViewHTML::renderFileHeader(*OS);
> + }
> +
> // Show functions
> for (const auto &Function : Coverage->getCoveredFunctions()) {
> if (!Filters.matches(Function))
> @@ -435,12 +493,13 @@
> outs() << "\n";
> continue;
> }
> - ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
> - << ":";
> - outs() << "\n";
> - mainView->render(outs(), /*WholeFile=*/false);
> - outs() << "\n";
> + mainView->render(*OS, /*WholeFile=*/false, /*RenderTitle=*/true);
> + *OS << "\n";
> }
> +
> + if (isHTML) {
> + SourceCoverageViewHTML::renderFileFooter(*OS);
> + }
> return 0;
> }
>
> @@ -453,21 +512,25 @@
> SourceFiles.push_back(Filename);
>
> for (const auto &SourceFile : SourceFiles) {
> + auto Basename = sys::path::parent_path(SourceFile);
> + sys::fs::create_directories(OutputDirectory + Basename);
> + std::string OutputPath = OutputDirectory;
> + if (OutputPath != "") {
> + OutputPath += "/" + std::string(SourceFile) + "." + FileExt;
> + }
> + auto OS = getStreamForFile(OutputPath);
> + if (!OS)
> + return 1;
> auto mainView = createSourceFileView(SourceFile, *Coverage);
> if (!mainView) {
> - ViewOpts.colored_ostream(outs(), raw_ostream::RED)
> + ViewOpts.colored_ostream(errs(), raw_ostream::RED)
> << "warning: The file '" << SourceFile << "' isn't covered.";
> - outs() << "\n";
> + errs() << "\n";
> continue;
> }
> -
> - if (ShowFilenames) {
> - ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
> - outs() << "\n";
> - }
> - mainView->render(outs(), /*Wholefile=*/true);
> + mainView->render(*OS, /*Wholefile=*/true, /*RenderTitle=*/ShowFilenames);
> if (SourceFiles.size() > 1)
> - outs() << "\n";
> + *OS << "\n";
> }
>
> return 0;
> Index: tools/llvm-cov/CoverageViewOptions.h
> ===================================================================
> --- tools/llvm-cov/CoverageViewOptions.h (revision 262364)
> +++ tools/llvm-cov/CoverageViewOptions.h (working copy)
> @@ -16,6 +16,12 @@
>
> /// \brief The options for displaying the code coverage information.
> struct CoverageViewOptions {
> + enum OutputFormat {
> + /// \brief ASCII table output.
> + OFConsole,
> + /// \brief An HTML directory.
> + OFHTML
> + };
> bool Debug;
> bool Colors;
> bool ShowLineNumbers;
> @@ -25,6 +31,7 @@
> bool ShowExpandedRegions;
> bool ShowFunctionInstantiations;
> bool ShowFullFilenames;
> + OutputFormat Format;
>
> /// \brief Change the output's stream color if the colors are enabled.
> ColoredRawOstream colored_ostream(raw_ostream &OS,
> Index: tools/llvm-cov/SourceCoverageView.cpp
> ===================================================================
> --- tools/llvm-cov/SourceCoverageView.cpp (revision 262364)
> +++ tools/llvm-cov/SourceCoverageView.cpp (working copy)
> @@ -12,13 +12,118 @@
> //===----------------------------------------------------------------------===//
>
> #include "SourceCoverageView.h"
> -#include "llvm/ADT/Optional.h"
> #include "llvm/ADT/SmallString.h"
> #include "llvm/ADT/StringExtras.h"
> -#include "llvm/Support/LineIterator.h"
>
> using namespace llvm;
>
> +namespace html {
> +/// An HTML-escaped string.
> +std::string escape(std::string Str) {
> + std::string Result;
> + for (size_t i = 0; i < Str.size(); ++i) {
> + std::string Token = Str.substr(i, 1);
> + if (Token == "&")
> + Token = "&";
> + else if (Token == "<")
> + Token = "<";
> + else if (Token == "\"")
> + Token = """;
> + else if (Token == ">")
> + Token = ">";
> + Result += Token;
> + }
> + return Result;
> +}
> +
> +/// Creates a tag around the provided text
> +/// (expects the value to be properly escaped).
> +std::string tag(std::string Name, std::string Text,
> + std::string ClassName = "") {
> + std::string Tag = "<" + Name;
> + if (ClassName != "") {
> + Tag += " class='" + ClassName + "'";
> + }
> + return Tag + ">" + Text + "</" + Name + ">";
> +}
> +}
> +
> +std::string classForColor(raw_ostream::Colors Color) {
> + switch (Color) {
> + case raw_ostream::RED:
> + return "red";
> + case raw_ostream::CYAN:
> + return "cyan";
> + case raw_ostream::BLUE:
> + return "blue";
> + case raw_ostream::BLACK:
> + return "black";
> + case raw_ostream::GREEN:
> + return "green";
> + case raw_ostream::MAGENTA:
> + return "magenta";
> + default:
> + return "clear";
> + }
> +}
> +
> +/// Format a count using engineering notation with 3 significant digits.
> +std::string formatCount(uint64_t N) {
> + std::string Number = utostr(N);
> + int Len = Number.size();
> + if (Len <= 3)
> + return Number;
> + int IntLen = Len % 3 == 0 ? 3 : Len % 3;
> + std::string Result(Number.data(), IntLen);
> + if (IntLen != 3) {
> + Result.push_back('.');
> + Result += Number.substr(IntLen, 3 - IntLen);
> + }
> + Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);
> + return Result;
> +}
> +
> +//===----------------------------------------------------------------------===//
> +//
> +// SourceCoverageView
> +//
> +//===----------------------------------------------------------------------===//
> +
> +std::unique_ptr<SourceCoverageView> SourceCoverageView::create(
> + const MemoryBuffer &File, const CoverageViewOptions &Options,
> + StringRef SourceName, coverage::CoverageData &&CoverageInfo) {
> + switch (Options.Format) {
> + case CoverageViewOptions::OFHTML:
> + return make_unique<SourceCoverageViewHTML>(File, Options, SourceName,
> + std::move(CoverageInfo));
> + case CoverageViewOptions::OFConsole:
> + return make_unique<SourceCoverageViewConsole>(File, Options, SourceName,
> + std::move(CoverageInfo));
> + }
> +}
> +
> +void SourceCoverageView::renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubview, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth) {
> +
> + if (Options.Debug)
> + errs() << "Expansion at line " << ExpansionView.getLine() << ", "
> + << ExpansionView.getStartCol() << " -> " << ExpansionView.getEndCol()
> + << "\n";
> + ExpansionView.View->render(OS, /*WholeFile=*/false,
> + /*RenderTitle*/ false, NestedIndent);
> +}
> +
> +void SourceCoverageView::renderInstantiationView(
> + raw_ostream &OS, InstantiationView &InstantiationView, unsigned IndentLevel,
> + unsigned NestedIndent, unsigned DividerWidth) {
> + InstantiationView.View->render(OS, /*WholeFile=*/false,
> + /*RenderTitle=*/true, NestedIndent);
> +}
> +
> void SourceCoverageView::renderLine(
> raw_ostream &OS, StringRef Line, int64_t LineNumber,
> const coverage::CoverageSegment *WrappedSegment,
> @@ -35,9 +140,8 @@
> unsigned Col = 1;
> for (const auto *S : Segments) {
> unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
> - colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
> - Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
> - << Line.substr(Col - 1, End - Col);
> + auto Text = Line.substr(Col - 1, End - Col);
> + output(OS, Text, Highlight, S);
> if (Options.Debug && Highlight)
> HighlightedRanges.push_back(std::make_pair(Col, End));
> Col = End;
> @@ -49,11 +153,8 @@
> Highlight = None;
> }
>
> - // Show the rest of the line
> - colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
> - Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
> - << Line.substr(Col - 1, Line.size() - Col + 1);
> - OS << "\n";
> + auto Rest = Line.substr(Col - 1, Line.size() - Col + 1);
> + output(OS, Rest, Highlight, nullptr);
>
> if (Options.Debug) {
> for (const auto &Range : HighlightedRanges)
> @@ -64,94 +165,13 @@
> }
> }
>
> -void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) {
> - for (unsigned I = 0; I < Level; ++I)
> - OS << " |";
> -}
> -
> -void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length,
> - raw_ostream &OS) {
> - assert(Level != 0 && "Cannot render divider at top level");
> - renderIndent(OS, Level - 1);
> - OS.indent(2);
> - for (unsigned I = 0; I < Length; ++I)
> - OS << "-";
> -}
> -
> -/// Format a count using engineering notation with 3 significant digits.
> -static std::string formatCount(uint64_t N) {
> - std::string Number = utostr(N);
> - int Len = Number.size();
> - if (Len <= 3)
> - return Number;
> - int IntLen = Len % 3 == 0 ? 3 : Len % 3;
> - std::string Result(Number.data(), IntLen);
> - if (IntLen != 3) {
> - Result.push_back('.');
> - Result += Number.substr(IntLen, 3 - IntLen);
> - }
> - Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);
> - return Result;
> -}
> -
> -void
> -SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
> - const LineCoverageInfo &Line) {
> - if (!Line.isMapped()) {
> - OS.indent(LineCoverageColumnWidth) << '|';
> - return;
> - }
> - std::string C = formatCount(Line.ExecutionCount);
> - OS.indent(LineCoverageColumnWidth - C.size());
> - colored_ostream(OS, raw_ostream::MAGENTA,
> - Line.hasMultipleRegions() && Options.Colors)
> - << C;
> - OS << '|';
> -}
> -
> -void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
> - unsigned LineNo) {
> - SmallString<32> Buffer;
> - raw_svector_ostream BufferOS(Buffer);
> - BufferOS << LineNo;
> - auto Str = BufferOS.str();
> - // Trim and align to the right
> - Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
> - OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
> -}
> -
> -void SourceCoverageView::renderRegionMarkers(
> - raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) {
> - unsigned PrevColumn = 1;
> - for (const auto *S : Segments) {
> - if (!S->IsRegionEntry)
> - continue;
> - // Skip to the new region
> - if (S->Col > PrevColumn)
> - OS.indent(S->Col - PrevColumn);
> - PrevColumn = S->Col + 1;
> - std::string C = formatCount(S->Count);
> - PrevColumn += C.size();
> - OS << '^' << C;
> - }
> - OS << "\n";
> -
> - if (Options.Debug)
> - for (const auto *S : Segments)
> - errs() << "Marker at " << S->Line << ":" << S->Col << " = "
> - << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
> -}
> -
> +#include <unistd.h>
> void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
> - unsigned IndentLevel) {
> + bool RenderTitle, unsigned IndentLevel) {
> // The width of the leading columns
> unsigned CombinedColumnWidth =
> (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
> (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
> - // The width of the line that is used to divide between the view and the
> - // subviews.
> - unsigned DividerWidth = CombinedColumnWidth + 4;
> -
> // We need the expansions and instantiations sorted so we can go through them
> // while we iterate lines.
> std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end());
> @@ -191,10 +211,10 @@
> LineCount.addRegionCount(WrappedSegment->Count);
> for (const auto *S : LineSegments)
> if (S->HasCount && S->IsRegionEntry)
> - LineCount.addRegionStartCount(S->Count);
> + LineCount.addRegionStartCount(S->Count);
>
> - // Render the line prefix.
> - renderIndent(OS, IndentLevel);
> + startLine(OS, IndentLevel);
> +
> if (Options.ShowLineStats)
> renderLineCoverageColumn(OS, LineCount);
> if (Options.ShowLineNumbers)
> @@ -202,8 +222,7 @@
>
> // If there are expansion subviews, we want to highlight the first one.
> unsigned ExpansionColumn = 0;
> - if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
> - Options.Colors)
> + if (NextESV != EndESV && NextESV->getLine() == LI.line_number())
> ExpansionColumn = NextESV->getStartCol();
>
> // Display the source code for the current line.
> @@ -214,51 +233,339 @@
> if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers ||
> LineCount.hasMultipleRegions()) &&
> !LineSegments.empty()) {
> - renderIndent(OS, IndentLevel);
> - OS.indent(CombinedColumnWidth);
> - renderRegionMarkers(OS, LineSegments);
> + renderRegionMarkers(OS, LineSegments, IndentLevel, CombinedColumnWidth);
> +
> + if (Options.Debug)
> + for (const auto *S : LineSegments)
> + errs() << "Marker at " << S->Line << ":" << S->Col << " = "
> + << formatCount(S->Count)
> + << (S->IsRegionEntry ? "\n" : " (pop)\n");
> }
> + // The width of the line that is used to divide between the view and the
> + // subviews.
> + unsigned DividerWidth = CombinedColumnWidth + 4;
>
> // Show the expansions and instantiations for this line.
> unsigned NestedIndent = IndentLevel + 1;
> bool RenderedSubView = false;
> for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
> ++NextESV) {
> - renderViewDivider(NestedIndent, DividerWidth, OS);
> - OS << "\n";
> - if (RenderedSubView) {
> - // Re-render the current line and highlight the expansion range for
> - // this subview.
> - ExpansionColumn = NextESV->getStartCol();
> - renderIndent(OS, IndentLevel);
> - OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
> - renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
> - ExpansionColumn);
> - renderViewDivider(NestedIndent, DividerWidth, OS);
> - OS << "\n";
> - }
> - // Render the child subview
> - if (Options.Debug)
> - errs() << "Expansion at line " << NextESV->getLine() << ", "
> - << NextESV->getStartCol() << " -> " << NextESV->getEndCol()
> - << "\n";
> - NextESV->View->render(OS, false, NestedIndent);
> + renderExpansionView(OS, LI, *NextESV, WrappedSegment, LineSegments,
> + RenderedSubView, IndentLevel, NestedIndent,
> + CombinedColumnWidth, DividerWidth);
> RenderedSubView = true;
> }
> for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
> - renderViewDivider(NestedIndent, DividerWidth, OS);
> - OS << "\n";
> - renderIndent(OS, NestedIndent);
> - OS << ' ';
> - Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName
> - << ":";
> - OS << "\n";
> - NextISV->View->render(OS, false, NestedIndent);
> + renderInstantiationView(OS, *NextISV, IndentLevel, NestedIndent,
> + DividerWidth);
> RenderedSubView = true;
> }
> if (RenderedSubView) {
> - renderViewDivider(NestedIndent, DividerWidth, OS);
> - OS << "\n";
> + finishRenderingSubviews(OS, NestedIndent, DividerWidth);
> }
> +
> + endLine(OS);
> }
> }
> +
> +void SourceCoverageView::renderRegionMarkers(
> + raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned IndentLevel, unsigned CombinedColumnWidth) {
> + OS.indent(CombinedColumnWidth);
> + unsigned PrevColumn = 1;
> + for (const auto *S : Segments) {
> + if (!S->IsRegionEntry)
> + continue;
> + // Skip to the new region
> + if (S->Col > PrevColumn)
> + OS.indent(S->Col - PrevColumn);
> + PrevColumn = S->Col + 1;
> + std::string C = formatCount(S->Count);
> + PrevColumn += C.size();
> + OS << '^' << C;
> + }
> + OS << "\n";
> +}
> +
> +//===----------------------------------------------------------------------===//
> +//
> +// SourceCoverageViewHTML
> +//
> +//===----------------------------------------------------------------------===//
> +
> +void SourceCoverageViewHTML::output(raw_ostream &OS, StringRef Text,
> + Optional<raw_ostream::Colors> Highlight,
> + const coverage::CoverageSegment *Segment) {
> + auto Color = Highlight ? *Highlight : raw_ostream::SAVEDCOLOR;
> + std::string EscapedText = html::escape(Text);
> + if (Highlight) {
> + EscapedText = html::tag("span", EscapedText, classForColor(Color));
> + }
> + if (Options.ShowRegionMarkers && Segment && Segment->IsRegionEntry) {
> + std::string C = formatCount(Segment->Count);
> + auto countTag = html::tag("span", html::escape(C));
> + EscapedText = html::tag("span", EscapedText + countTag, "tooltips");
> + }
> + OS << EscapedText;
> +}
> +
> +void SourceCoverageViewHTML::renderLine(
> + raw_ostream &OS, StringRef Line, int64_t LineNumber,
> + const coverage::CoverageSegment *WrappedSegment,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned int ExpansionCol) {
> + OS << "<td><pre>\n";
> + SourceCoverageView::renderLine(OS, Line, LineNumber, WrappedSegment, Segments,
> + ExpansionCol);
> + OS << "</pre>\n";
> + if (ExpansionSubViews.empty() && InstantiationSubViews.empty()) {
> + // don't end the source line if we're going to embed sub-tables.
> + OS << "</td>\n";
> + }
> +}
> +
> +void SourceCoverageViewHTML::renderTitle(raw_ostream &OS) {
> + OS << "<div class='function-title'><pre>" << std::string(SourceName)
> + << ":</pre></div>";
> +}
> +
> +void SourceCoverageViewHTML::renderLineCoverageColumn(
> + raw_ostream &OS, const LineCoverageInfo &Line) {
> + if (Line.isMapped()) {
> + std::string C = formatCount(Line.ExecutionCount);
> + OS << html::tag("td", html::tag("pre", html::escape(C)), "numeric") << "\n";
> + } else {
> + OS << html::tag("td", "", "numeric") << "\n";
> + }
> +}
> +
> +void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
> + unsigned int LineNo) {
> + SmallString<32> Buffer;
> + raw_svector_ostream BufferOS(Buffer);
> + BufferOS << LineNo;
> + auto Str = BufferOS.str();
> + OS << html::tag("td", html::tag("pre", Str), "numeric") << "\n";
> +}
> +
> +void SourceCoverageViewHTML::renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubView, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth) {
> + OS << "<div class='expansion-view'>\n";
> + // Render the child subview
> +
> + if (RenderedSubView) {
> + // Re-render the current line and highlight the expansion range for
> + // this subview.
> + unsigned ExpansionColumn = ExpansionView.getStartCol();
> + OS << "<pre>";
> + SourceCoverageView::renderLine(OS, *LI, LI.line_number(), WrappedSegment,
> + LineSegments, ExpansionColumn);
> + OS << "</pre>\n";
> + }
> +
> + SourceCoverageView::renderExpansionView(
> + OS, LI, ExpansionView, WrappedSegment, LineSegments, RenderedSubView,
> + IndentLevel, NestedIndent, CombinedColumnWidth, DividerWidth);
> +
> + OS << "</div>\n";
> +}
> +
> +void SourceCoverageViewHTML::renderInstantiationView(
> + llvm::raw_ostream &OS, llvm::InstantiationView &InstantiationView,
> + unsigned IndentLevel, unsigned NestedIndent, unsigned DividerWidth) {
> + OS << "<div class='expansion-view'>\n";
> + SourceCoverageView::renderInstantiationView(
> + OS, InstantiationView, IndentLevel, NestedIndent, DividerWidth);
> + OS << "</div>\n";
> +}
> +
> +void SourceCoverageViewHTML::finishRenderingSubviews(raw_ostream &OS,
> + unsigned IndentLevel,
> + unsigned DividerWidth) {
> + OS << "</td>\n";
> +}
> +
> +void SourceCoverageViewHTML::renderFileHeader(raw_ostream &OS) {
> +// bring in the `CoverageCSS` string declared in CoverageCSS.inc
> +#include "CoverageCSS.inc"
> +
> + OS << "<!doctype html>\n"
> + "<html>\n"
> + " <head>\n"
> + " <meta name='viewport'"
> + "content='width=device-width,initial-scale=1'>\n"
> + " <style>\n"
> + << CoverageCSS << " </style>\n"
> + " </head>\n"
> + " <body>\n";
> +}
> +
> +void SourceCoverageViewHTML::renderFileFooter(raw_ostream &OS) {
> + OS << " </body>\n"
> + "</html>\n";
> +}
> +
> +void SourceCoverageViewHTML::render(raw_ostream &OS, bool WholeFile,
> + bool RenderTitle, unsigned IndentLevel) {
> + std::string tableDecl;
> +
> + if (WholeFile) {
> + renderFileHeader(OS);
> + tableDecl = "<table class='centered'>\n";
> + } else {
> + tableDecl = "<table>\n";
> + }
> +
> + if (RenderTitle)
> + renderTitle(OS);
> +
> + OS << tableDecl;
> + SourceCoverageView::render(OS, WholeFile, IndentLevel);
> + OS << "</table>\n";
> + if (WholeFile) {
> + renderFileFooter(OS);
> + }
> +}
> +
> +void SourceCoverageViewHTML::startLine(raw_ostream &OS, unsigned IndentLevel) {
> + OS << "<tr>\n";
> +}
> +
> +void SourceCoverageViewHTML::endLine(raw_ostream &OS) { OS << "</tr>\n"; }
> +
> +//===----------------------------------------------------------------------===//
> +//
> +// SourceCoverageViewConsole
> +//
> +//===----------------------------------------------------------------------===//
> +
> +void SourceCoverageViewConsole::output(
> + raw_ostream &OS, StringRef Text, Optional<raw_ostream::Colors> Highlight,
> + const coverage::CoverageSegment *Segment) {
> + auto Color = Highlight ? *Highlight : raw_ostream::SAVEDCOLOR;
> + colored_ostream(OS, Color, Options.Colors && Highlight, /*Bold=*/false,
> + /*BG=*/true)
> + << Text;
> +}
> +
> +void SourceCoverageViewConsole::render(raw_ostream &OS, bool WholeFile,
> + bool RenderTitle, unsigned IndentLevel) {
> + if (RenderTitle)
> + renderTitle(OS);
> + SourceCoverageView::render(OS, WholeFile, RenderTitle, IndentLevel);
> +}
> +
> +void SourceCoverageViewConsole::renderTitle(raw_ostream &OS) {
> + Options.colored_ostream(OS, raw_ostream::CYAN) << SourceName << ":\n";
> +}
> +
> +void SourceCoverageViewConsole::renderLine(
> + raw_ostream &OS, StringRef Line, int64_t LineNumber,
> + const coverage::CoverageSegment *WrappedSegment,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned int ExpansionCol) {
> + SourceCoverageView::renderLine(OS, Line, LineNumber, WrappedSegment, Segments,
> + ExpansionCol);
> + OS << "\n";
> +}
> +
> +void SourceCoverageViewConsole::renderIndent(raw_ostream &OS, unsigned Level) {
> + for (unsigned I = 0; I < Level; ++I)
> + OS << " |";
> +}
> +
> +void SourceCoverageViewConsole::renderViewDivider(unsigned Level,
> + unsigned Length,
> + raw_ostream &OS) {
> + assert(Level != 0 && "Cannot render divider at top level");
> + renderIndent(OS, Level - 1);
> + OS.indent(2);
> + for (unsigned I = 0; I < Length; ++I)
> + OS << "-";
> +}
> +
> +void SourceCoverageViewConsole::renderLineCoverageColumn(
> + raw_ostream &OS, const LineCoverageInfo &Line) {
> +
> + std::string C = formatCount(Line.ExecutionCount);
> + if (!Line.isMapped()) {
> + OS.indent(LineCoverageColumnWidth) << '|';
> + return;
> + }
> + OS.indent(LineCoverageColumnWidth - C.size());
> + colored_ostream(OS, raw_ostream::MAGENTA,
> + Line.hasMultipleRegions() && Options.Colors)
> + << C;
> + OS << '|';
> +}
> +
> +void SourceCoverageViewConsole::renderLineNumberColumn(raw_ostream &OS,
> + unsigned LineNo) {
> + SmallString<32> Buffer;
> + raw_svector_ostream BufferOS(Buffer);
> + BufferOS << LineNo;
> + auto Str = BufferOS.str();
> + // Trim and align to the right
> + Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
> + OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
> +}
> +
> +void SourceCoverageViewConsole::renderRegionMarkers(
> + raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned IndentLevel, unsigned CombinedColumnWidth) {
> + renderIndent(OS, IndentLevel);
> + SourceCoverageView::renderRegionMarkers(OS, Segments, IndentLevel,
> + CombinedColumnWidth);
> +}
> +
> +void SourceCoverageViewConsole::startLine(raw_ostream &OS,
> + unsigned IndentLevel) {
> + renderIndent(OS, IndentLevel);
> +}
> +
> +void SourceCoverageViewConsole::renderInstantiationView(
> + raw_ostream &OS, InstantiationView &InstantiationView, unsigned IndentLevel,
> + unsigned NestedIndent, unsigned DividerWidth) {
> + renderViewDivider(NestedIndent, DividerWidth, OS);
> + OS << "\n";
> + renderIndent(OS, NestedIndent);
> + OS << ' ';
> + SourceCoverageView::renderInstantiationView(
> + OS, InstantiationView, IndentLevel, NestedIndent, DividerWidth);
> +}
> +
> +void SourceCoverageViewConsole::finishRenderingSubviews(raw_ostream &OS,
> + unsigned IndentLevel,
> + unsigned DividerWidth) {
> + renderViewDivider(IndentLevel, DividerWidth, OS);
> + OS << "\n";
> +}
> +
> +void SourceCoverageViewConsole::renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubView, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth) {
> +
> + renderViewDivider(NestedIndent, DividerWidth, OS);
> + OS << "\n";
> + if (RenderedSubView) {
> + // Re-render the current line and highlight the expansion range for
> + // this subview.
> + unsigned ExpansionColumn = ExpansionView.getStartCol();
> + renderIndent(OS, IndentLevel);
> + OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
> + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
> + ExpansionColumn);
> + renderViewDivider(NestedIndent, DividerWidth, OS);
> + OS << "\n";
> + }
> + SourceCoverageView::renderExpansionView(
> + OS, LI, ExpansionView, WrappedSegment, LineSegments, RenderedSubView,
> + IndentLevel, NestedIndent, CombinedColumnWidth, DividerWidth);
> +}
> Index: tools/llvm-cov/SourceCoverageView.h
> ===================================================================
> --- tools/llvm-cov/SourceCoverageView.h (revision 262364)
> +++ tools/llvm-cov/SourceCoverageView.h (working copy)
> @@ -17,6 +17,9 @@
> #include "CoverageViewOptions.h"
> #include "llvm/ProfileData/CoverageMapping.h"
> #include "llvm/Support/MemoryBuffer.h"
> +#include "llvm/Support/LineIterator.h"
> +#include "llvm/ADT/Optional.h"
> +#include "llvm/ADT/STLExtras.h"
> #include <vector>
>
> namespace llvm {
> @@ -76,7 +79,7 @@
> /// \brief A code coverage view of a specific source file.
> /// It can have embedded coverage views.
> class SourceCoverageView {
> -private:
> +protected:
> /// \brief Coverage information for a single line.
> struct LineCoverageInfo {
> uint64_t ExecutionCount;
> @@ -103,40 +106,70 @@
>
> const MemoryBuffer &File;
> const CoverageViewOptions &Options;
> + StringRef SourceName;
> coverage::CoverageData CoverageInfo;
> std::vector<ExpansionView> ExpansionSubViews;
> std::vector<InstantiationView> InstantiationSubViews;
>
> + virtual void output(llvm::raw_ostream &OS, StringRef Text,
> + llvm::Optional<llvm::raw_ostream::Colors> Highlight,
> + const coverage::CoverageSegment *Segment = nullptr) = 0;
> +
> /// \brief Render a source line with highlighting.
> - void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
> - const coverage::CoverageSegment *WrappedSegment,
> - ArrayRef<const coverage::CoverageSegment *> Segments,
> - unsigned ExpansionCol);
> + virtual void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
> + const coverage::CoverageSegment *WrappedSegment,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned ExpansionCol);
>
> - void renderIndent(raw_ostream &OS, unsigned Level);
> -
> - void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
> -
> /// \brief Render the line's execution count column.
> - void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);
> + virtual void renderLineCoverageColumn(raw_ostream &OS,
> + const LineCoverageInfo &Line) = 0;
>
> /// \brief Render the line number column.
> - void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
> + virtual void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) = 0;
>
> /// \brief Render all the region's execution counts on a line.
> - void
> + virtual void
> renderRegionMarkers(raw_ostream &OS,
> - ArrayRef<const coverage::CoverageSegment *> Segments);
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned IndentLevel, unsigned CombinedColumnWidth);
>
> + /// \brief Render an expansion view inline, using format-specific logic.
> + virtual void renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubView, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth);
> +
> + /// \brief Render an instantiation view inline, using format-specific logic.
> + virtual void renderInstantiationView(raw_ostream &OS,
> + InstantiationView &InstantiationView,
> + unsigned IndentLevel,
> + unsigned NestedIndent,
> + unsigned DividerWidth);
> +
> + /// \brief Render all the region's execution counts on a line.
> + virtual void finishRenderingSubviews(raw_ostream &OS, unsigned IndentLevel,
> + unsigned DividerWidth) = 0;
> +
> + virtual void startLine(raw_ostream &OS, unsigned IndentLevel) {}
> + virtual void endLine(raw_ostream &OS) {}
> +
> static const unsigned LineCoverageColumnWidth = 7;
> static const unsigned LineNumberColumnWidth = 5;
>
> public:
> SourceCoverageView(const MemoryBuffer &File,
> - const CoverageViewOptions &Options,
> + const CoverageViewOptions &Options, StringRef SourceName,
> coverage::CoverageData &&CoverageInfo)
> - : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {}
> + : File(File), Options(Options), SourceName(SourceName),
> + CoverageInfo(std::move(CoverageInfo)) {}
>
> + static std::unique_ptr<SourceCoverageView>
> + create(const MemoryBuffer &File, const CoverageViewOptions &Options,
> + StringRef SourceName, coverage::CoverageData &&CoverageInfo);
> +
> const CoverageViewOptions &getOptions() const { return Options; }
>
> /// \brief Add an expansion subview to this view.
> @@ -153,9 +186,144 @@
>
> /// \brief Print the code coverage information for a specific
> /// portion of a source file to the output stream.
> - void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0);
> + virtual void render(raw_ostream &OS, bool WholeFile, bool RenderTitle = false,
> + unsigned IndentLevel = 0);
> +
> + /// \brief Render the title for a given source region (function or file name)
> + virtual void renderTitle(raw_ostream &OS) = 0;
> +
> + virtual ~SourceCoverageView() {}
> };
>
> +class SourceCoverageViewHTML : public SourceCoverageView {
> +
> + /// \brief Render a source line with highlighting.
> + void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
> + const coverage::CoverageSegment *WrappedSegment,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned ExpansionCol) override;
> +
> + void startLine(raw_ostream &OS, unsigned IndentLevel) override;
> + void endLine(raw_ostream &OS) override;
> +
> + /// \brief Render the line's execution count column.
> + void renderLineCoverageColumn(raw_ostream &OS,
> + const LineCoverageInfo &Line) override;
> +
> + void output(llvm::raw_ostream &OS, StringRef Text,
> + llvm::Optional<llvm::raw_ostream::Colors> Highlight,
> + const coverage::CoverageSegment *Segment = nullptr) override;
> +
> + /// \brief Render the line number column.
> + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
> +
> + /// \brief Render all the region's execution counts on a line.
> + void renderRegionMarkers(raw_ostream &OS,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned IndentLevel,
> + unsigned CombinedColumnWidth) override {
> + // empty implementation (region markers are tooltips).
> + }
> +
> + /// \brief Print the code coverage information for a specific
> + /// portion of a source file to the output stream.
> + void render(raw_ostream &OS, bool WholeFile, bool RenderTitle = false,
> + unsigned IndentLevel = 0) override;
> +
> + void finishRenderingSubviews(raw_ostream &OS, unsigned IndentLevel,
> + unsigned DividerWidth) override;
> + void renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubView, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth) override;
> +
> + void renderInstantiationView(raw_ostream &OS,
> + InstantiationView &InstantiationView,
> + unsigned IndentLevel, unsigned NestedIndent,
> + unsigned DividerWidth) override;
> +
> +public:
> + SourceCoverageViewHTML(const MemoryBuffer &File,
> + const CoverageViewOptions &Options,
> + StringRef SourceName,
> + coverage::CoverageData &&CoverageInfo)
> + : SourceCoverageView(File, Options, SourceName, std::move(CoverageInfo)) {
> + }
> +
> + // \brief Render the standard boilerplate HTML header.
> + static void renderFileHeader(raw_ostream &OS);
> +
> + // \brief Render the standard boilerplate HTML footer.
> + static void renderFileFooter(raw_ostream &OS);
> +
> + /// \brief Render the title for a given source region (function or file name)
> + void renderTitle(raw_ostream &OS) override;
> +};
> +
> +class SourceCoverageViewConsole : public SourceCoverageView {
> +
> + /// \brief Render a source line with highlighting.
> + void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
> + const coverage::CoverageSegment *WrappedSegment,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned ExpansionCol) override;
> +
> + void startLine(raw_ostream &OS, unsigned IndentLevel) override;
> +
> + /// \brief Render the line's execution count column.
> + void renderLineCoverageColumn(raw_ostream &OS,
> + const LineCoverageInfo &Line) override;
> +
> + void output(raw_ostream &OS, StringRef Text,
> + Optional<raw_ostream::Colors> Highlight,
> + const coverage::CoverageSegment *Segment = nullptr) override;
> +
> + /// \brief Render the line number column.
> + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
> +
> + /// \brief Render all the region's execution counts on a line.
> + void renderRegionMarkers(raw_ostream &OS,
> + ArrayRef<const coverage::CoverageSegment *> Segments,
> + unsigned IndentLevel,
> + unsigned CombinedColumnWidth) override;
> +
> + void renderIndent(raw_ostream &OS, unsigned Level);
> +
> + void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
> +
> + void finishRenderingSubviews(raw_ostream &OS, unsigned IndentLevel,
> + unsigned DividerWidth) override;
> + void renderExpansionView(
> + raw_ostream &OS, line_iterator &LI, ExpansionView &ExpansionView,
> + const coverage::CoverageSegment *WrappedSegment,
> + SmallVector<const coverage::CoverageSegment *, 8> LineSegments,
> + bool RenderedSubView, unsigned IndentLevel, unsigned NestedIndent,
> + unsigned CombinedColumnWidth, unsigned DividerWidth) override;
> +
> + void renderInstantiationView(raw_ostream &OS,
> + InstantiationView &InstantiationView,
> + unsigned IndentLevel, unsigned NestedIndent,
> + unsigned DividerWidth) override;
> +
> + /// \brief Print the code coverage information for a specific
> + /// portion of a source file to the output stream.
> + void render(raw_ostream &OS, bool WholeFile, bool RenderTitle = false,
> + unsigned IndentLevel = 0) override;
> +
> +public:
> + SourceCoverageViewConsole(const MemoryBuffer &File,
> + const CoverageViewOptions &Options,
> + StringRef SourceName,
> + coverage::CoverageData &&CoverageInfo)
> + : SourceCoverageView(File, Options, SourceName, std::move(CoverageInfo)) {
> + }
> +
> + /// \brief Render the title for a given source region (function or file name)
> + void renderTitle(raw_ostream &OS) override;
> +};
> +
> } // namespace llvm
>
> #endif // LLVM_COV_SOURCECOVERAGEVIEW_H
> On Mar 2, 2016, at 3:00 PM, Harlan Haskins via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
> Oh, I see! Yeah, this seems like something I overlooked. The HTML view
> currently just shows two expansions, one after the other.
>
> I’ll revise the subview rendering with this in mind. Thanks for the simple
> example case!
>
> Best,
> Harlan
>
> On Mar 2, 2016, at 2:40 PM, Xinliang David Li via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
> On Wed, Mar 2, 2016 at 1:41 PM, Harlan Haskins <hhaskins at apple.com>
> wrote:
>
> Hi David,
>
> Specifically with renderSubviews, in my refactor it seemed that
> their bodies were different enough (i.e. SourceCoverageViewConsole
> needs to track state between loop invocations to know whether or
> not to display a final view divider, and the common behavior is
> really just looping over the instantiation and expansion subviews.
> I can investigate converging them more, but I think it’s going to
> increase complexity.
>
> Emitting the final divider is easy to abstract away. The main
> difference I see is that in Console view, if there are multiple macro
> expansions in the same line, the line will be re-rendered again in
> order to highlight the macro. For instance given the following line
> with two macros,
>
> MY_MACRO(10, 10); MY_MACRO(20,10); // line 10
>
> The console dump will be two lines:
>
> 1| 10| MY_MACRO(10, 10); MY_MACRO(20,10); // line 10
> ^^^^^^^^^^^
> .... expansion lines
>
> MY_MACRO(10, 10); MY_MACRO(20,10); // line 10
> ^^^^^^^^^^^^^
> .... expansion lines
>
> Does HTML view lose that functionality?
>
> Also the template instantiation subview rendering code looks
> sufficiently close between two classes.
>
> thanks,
>
> David
>
> Also, I definitely need to add a test case.
>
> Thanks!
> Harlan
>
> On Mar 2, 2016, at 10:43 AM, Xinliang David Li <
> xinliangli at gmail.com> wrote:
>
> Hi Harlan,
>
> This looks great! Some high level comments. I find the code
> can be further restructured
> 1) high level methods can be commoned between two derived
> classes (and pushed to the base class) -- such as
> renderSubviews
> 2) the subclasses just need to provide virtual functions that
> implement the view specific low level routines.
>
> Also there does not seem to be a test case.
>
> thanks,
>
> David
>
> On Tue, Mar 1, 2016 at 6:03 PM, Harlan Haskins via
> llvm-commits <llvm-commits at lists.llvm.org> wrote:
>
> Oops, forgot to add a file to the patch.
>
> New patch attached.
>
> On Mar 1, 2016, at 5:48 PM, Harlan Haskins via
> llvm-commits <llvm-commits at lists.llvm.org> wrote:
>
> Hi all,
>
> I’ve got a preliminary implementation of HTML
> generation for llvm-cov’s coverage reports.
>
> The patch adds 2 flags to llvm-cov show:
> = -format [html | console]
> = -output-dir <dirname>
>
> Specifying -format=console will perform the current
> behavior (now the default), and -format=html will
> generate an HTML report.
> If -output-dir is specified, then the output is split
> into one html or txt file per source file, named
> <source-name>.<ext>, with a directory structure that
> mimics the file system structure.
>
> If neither are provided, the behavior remains the
> same.
>
> I’m hoping to add an index with a browsable list of
> files within their directories, but for now I’ve
> attached the patch and a sample HTML file (In this
> case, AliasAnalysis.h, as included by swiftc).
>
> Thanks,
> Harlan Haskins
>
> <AliasAnalysis.h.html>
> <llvm-cov-html.diff>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/
> llvm-commits
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/
> llvm-commits
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list