| //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file This file implements the text-based coverage renderer. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "CoverageReport.h" | 
 | #include "SourceCoverageViewText.h" | 
 | #include "llvm/ADT/Optional.h" | 
 | #include "llvm/ADT/SmallString.h" | 
 | #include "llvm/ADT/StringExtras.h" | 
 |  | 
 | using namespace llvm; | 
 |  | 
 | Expected<CoveragePrinter::OwnedStream> | 
 | CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) { | 
 |   return createOutputStream(Path, "txt", InToplevel); | 
 | } | 
 |  | 
 | void CoveragePrinterText::closeViewFile(OwnedStream OS) { | 
 |   OS->operator<<('\n'); | 
 | } | 
 |  | 
 | Error CoveragePrinterText::createIndexFile( | 
 |     ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage, | 
 |     const CoverageFiltersMatchAll &Filters) { | 
 |   auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true); | 
 |   if (Error E = OSOrErr.takeError()) | 
 |     return E; | 
 |   auto OS = std::move(OSOrErr.get()); | 
 |   raw_ostream &OSRef = *OS.get(); | 
 |  | 
 |   CoverageReport Report(Opts, Coverage); | 
 |   Report.renderFileReports(OSRef, SourceFiles, Filters); | 
 |  | 
 |   Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n" | 
 |                                                  << Opts.getLLVMVersionString(); | 
 |  | 
 |   return Error::success(); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | static const unsigned LineCoverageColumnWidth = 7; | 
 | static const unsigned LineNumberColumnWidth = 5; | 
 |  | 
 | /// Get the width of the leading columns. | 
 | unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) { | 
 |   return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + | 
 |          (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0); | 
 | } | 
 |  | 
 | /// The width of the line that is used to divide between the view and | 
 | /// the subviews. | 
 | unsigned getDividerWidth(const CoverageViewOptions &Opts) { | 
 |   return getCombinedColumnWidth(Opts) + 4; | 
 | } | 
 |  | 
 | } // anonymous namespace | 
 |  | 
 | void SourceCoverageViewText::renderViewHeader(raw_ostream &) {} | 
 |  | 
 | void SourceCoverageViewText::renderViewFooter(raw_ostream &) {} | 
 |  | 
 | void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) { | 
 |   getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName() | 
 |                                                       << ":\n"; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS, | 
 |                                               unsigned ViewDepth) { | 
 |   for (unsigned I = 0; I < ViewDepth; ++I) | 
 |     OS << "  |"; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {} | 
 |  | 
 | void SourceCoverageViewText::renderViewDivider(raw_ostream &OS, | 
 |                                                unsigned ViewDepth) { | 
 |   assert(ViewDepth != 0 && "Cannot render divider at top level"); | 
 |   renderLinePrefix(OS, ViewDepth - 1); | 
 |   OS.indent(2); | 
 |   unsigned Length = getDividerWidth(getOptions()); | 
 |   for (unsigned I = 0; I < Length; ++I) | 
 |     OS << '-'; | 
 |   OS << '\n'; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L, | 
 |                                         const LineCoverageStats &LCS, | 
 |                                         unsigned ExpansionCol, | 
 |                                         unsigned ViewDepth) { | 
 |   StringRef Line = L.Line; | 
 |   unsigned LineNumber = L.LineNo; | 
 |   auto *WrappedSegment = LCS.getWrappedSegment(); | 
 |   CoverageSegmentArray Segments = LCS.getLineSegments(); | 
 |  | 
 |   Optional<raw_ostream::Colors> Highlight; | 
 |   SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; | 
 |  | 
 |   // The first segment overlaps from a previous line, so we treat it specially. | 
 |   if (WrappedSegment && !WrappedSegment->IsGapRegion && | 
 |       WrappedSegment->HasCount && WrappedSegment->Count == 0) | 
 |     Highlight = raw_ostream::RED; | 
 |  | 
 |   // Output each segment of the line, possibly highlighted. | 
 |   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, | 
 |                     getOptions().Colors && Highlight, /*Bold=*/false, | 
 |                     /*BG=*/true) | 
 |         << Line.substr(Col - 1, End - Col); | 
 |     if (getOptions().Debug && Highlight) | 
 |       HighlightedRanges.push_back(std::make_pair(Col, End)); | 
 |     Col = End; | 
 |     if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) && | 
 |         S->HasCount && S->Count == 0) | 
 |       Highlight = raw_ostream::RED; | 
 |     else if (Col == ExpansionCol) | 
 |       Highlight = raw_ostream::CYAN; | 
 |     else | 
 |       Highlight = None; | 
 |   } | 
 |  | 
 |   // Show the rest of the line. | 
 |   colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, | 
 |                   getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true) | 
 |       << Line.substr(Col - 1, Line.size() - Col + 1); | 
 |   OS << '\n'; | 
 |  | 
 |   if (getOptions().Debug) { | 
 |     for (const auto &Range : HighlightedRanges) | 
 |       errs() << "Highlighted line " << LineNumber << ", " << Range.first | 
 |              << " -> " << Range.second << '\n'; | 
 |     if (Highlight) | 
 |       errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; | 
 |   } | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderLineCoverageColumn( | 
 |     raw_ostream &OS, const LineCoverageStats &Line) { | 
 |   if (!Line.isMapped()) { | 
 |     OS.indent(LineCoverageColumnWidth) << '|'; | 
 |     return; | 
 |   } | 
 |   std::string C = formatCount(Line.getExecutionCount()); | 
 |   OS.indent(LineCoverageColumnWidth - C.size()); | 
 |   colored_ostream(OS, raw_ostream::MAGENTA, | 
 |                   Line.hasMultipleRegions() && getOptions().Colors) | 
 |       << C; | 
 |   OS << '|'; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::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 SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS, | 
 |                                                  const LineCoverageStats &Line, | 
 |                                                  unsigned ViewDepth) { | 
 |   renderLinePrefix(OS, ViewDepth); | 
 |   OS.indent(getCombinedColumnWidth(getOptions())); | 
 |  | 
 |   CoverageSegmentArray Segments = Line.getLineSegments(); | 
 |  | 
 |   // Just consider the segments which start *and* end on this line. | 
 |   if (Segments.size() > 1) | 
 |     Segments = Segments.drop_back(); | 
 |  | 
 |   unsigned PrevColumn = 1; | 
 |   for (const auto *S : Segments) { | 
 |     if (!S->IsRegionEntry) | 
 |       continue; | 
 |     if (S->Count == Line.getExecutionCount()) | 
 |       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; | 
 |  | 
 |     if (getOptions().Debug) | 
 |       errs() << "Marker at " << S->Line << ":" << S->Col << " = " | 
 |             << formatCount(S->Count) << "\n"; | 
 |   } | 
 |   OS << '\n'; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L, | 
 |                                                  const LineCoverageStats &LCS, | 
 |                                                  unsigned ExpansionCol, | 
 |                                                  unsigned ViewDepth) { | 
 |   renderLinePrefix(OS, ViewDepth); | 
 |   OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1)); | 
 |   renderLine(OS, L, LCS, ExpansionCol, ViewDepth); | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderExpansionView(raw_ostream &OS, | 
 |                                                  ExpansionView &ESV, | 
 |                                                  unsigned ViewDepth) { | 
 |   // Render the child subview. | 
 |   if (getOptions().Debug) | 
 |     errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol() | 
 |            << " -> " << ESV.getEndCol() << '\n'; | 
 |   ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false, | 
 |                   /*ShowTitle=*/false, ViewDepth + 1); | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS, | 
 |                                                      InstantiationView &ISV, | 
 |                                                      unsigned ViewDepth) { | 
 |   renderLinePrefix(OS, ViewDepth); | 
 |   OS << ' '; | 
 |   if (!ISV.View) | 
 |     getOptions().colored_ostream(OS, raw_ostream::RED) | 
 |         << "Unexecuted instantiation: " << ISV.FunctionName << "\n"; | 
 |   else | 
 |     ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, | 
 |                     /*ShowTitle=*/false, ViewDepth); | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) { | 
 |   if (getOptions().hasProjectTitle()) | 
 |     getOptions().colored_ostream(OS, raw_ostream::CYAN) | 
 |         << getOptions().ProjectTitle << "\n"; | 
 |  | 
 |   getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n"; | 
 |  | 
 |   if (getOptions().hasCreatedTime()) | 
 |     getOptions().colored_ostream(OS, raw_ostream::CYAN) | 
 |         << getOptions().CreatedTimeStr << "\n"; | 
 | } | 
 |  | 
 | void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned, | 
 |                                                unsigned) {} |