Index: test/tools/llvm-cov/native_separators.c =================================================================== --- test/tools/llvm-cov/native_separators.c +++ test/tools/llvm-cov/native_separators.c @@ -19,3 +19,9 @@ // HTML: tools\llvm-cov\Inputs\native_separators.covmapping int main() {} + +// Re-purpose this file to test that "Go to first unexecuted line" feature. + +// RUN: llvm-cov show %S/Inputs/native_separators.covmapping -instr-profile %t.profdata -filename-equivalence -format html -o %t.dir %s +// RUN: FileCheck -input-file %t.dir/coverage/tmp/native_separators.c.html %s +// CHECK:
Go to first unexecuted line
Index: test/tools/llvm-cov/showProjectSummary.cpp =================================================================== --- test/tools/llvm-cov/showProjectSummary.cpp +++ test/tools/llvm-cov/showProjectSummary.cpp @@ -24,9 +24,9 @@ // Test html output. // RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %s -// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s +// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER,HTML-UNCOVEREDLINE -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s // RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -project-title "Test Suite" -filename-equivalence %s -// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML,HTML-FILE,HTML-HEADER -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s +// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML,HTML-FILE,HTML-HEADER,HTML-UNCOVEREDLINE -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s // RUN: FileCheck -check-prefixes=HTML-TITLE,HTML -input-file %t.dir/index.html %s // RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -project-title "Test Suite" -filename-equivalence -name=main %s // RUN: FileCheck -check-prefixes=HTML-FUNCTION,HTML-HEADER -input-file %t.dir/functions.html %s @@ -41,6 +41,7 @@ // HTML-FILE:
Binary:
 // HTML-FILE: showProjectSummary.covmapping
// HTML-FUNCTION:
Function: main
+// HTML-UNCOVEREDLINE: Go to first unexecuted line // HTML-HEADER:
Line No.
// HTML-HEADER:
Count
// HTML-HEADER:
Source
Index: tools/llvm-cov/CodeCoverage.cpp =================================================================== --- tools/llvm-cov/CodeCoverage.cpp +++ tools/llvm-cov/CodeCoverage.cpp @@ -693,6 +693,12 @@ for (StringRef SourceFile : SourceFiles) { Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] { + FileCoverageSummary Summary(SourceFile); + for (const auto &F : Coverage->getCoveredFunctions(SourceFile)) { + FunctionCoverageSummary Function = FunctionCoverageSummary::get(F); + Summary.addFunction(Function); + } + bool IsFullyCovered = Summary.LineCoverage.isFullyCovered(); auto View = createSourceFileView(SourceFile, *Coverage); if (!View) { warning("The file '" + SourceFile.str() + "' isn't covered."); @@ -707,7 +713,8 @@ auto OS = std::move(OSOrErr.get()); View->print(*OS.get(), /*Wholefile=*/true, - /*ShowSourceName=*/ShowFilenames); + /*ShowSourceName=*/ShowFilenames, /*ViewDepth=*/0, + /*FullyCovered=*/IsFullyCovered); Printer->closeViewFile(std::move(OS)); }); } Index: tools/llvm-cov/SourceCoverageView.h =================================================================== --- tools/llvm-cov/SourceCoverageView.h +++ tools/llvm-cov/SourceCoverageView.h @@ -195,7 +195,8 @@ virtual void renderViewFooter(raw_ostream &OS) = 0; /// \brief Render the source name for the view. - virtual void renderSourceName(raw_ostream &OS, bool WholeFile) = 0; + virtual void renderSourceName(raw_ostream &OS, bool WholeFile, + bool FullyCovered, unsigned LineNo) = 0; /// \brief Render the line prefix at the given \p ViewDepth. virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0; @@ -289,7 +290,7 @@ /// \brief Print the code coverage information for a specific portion of a /// source file to the output stream. void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, - unsigned ViewDepth = 0); + unsigned ViewDepth = 0, bool FullyCovered = false); }; } // namespace llvm Index: tools/llvm-cov/SourceCoverageView.cpp =================================================================== --- tools/llvm-cov/SourceCoverageView.cpp +++ tools/llvm-cov/SourceCoverageView.cpp @@ -136,14 +136,43 @@ } void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, - bool ShowSourceName, unsigned ViewDepth) { + bool ShowSourceName, unsigned ViewDepth, + bool FullyCovered) { if (WholeFile) renderCellInTitle(OS, "Code Coverage Report"); renderViewHeader(OS); + unsigned FirstUncoveredLineNo = 0; + if (!FullyCovered) { + // Get the first uncovered line for the file. + auto NextSegment = CoverageInfo.begin(); + auto EndSegment = CoverageInfo.end(); + SmallVector LineSegments; + for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { + bool IsUncoveredLine = true; + // Collect the coverage information relevant to this line. + LineSegments.clear(); + while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) + LineSegments.push_back(&*NextSegment++); + + if (LineSegments.empty()) + continue; + for (const auto *S : LineSegments) + if (S->IsRegionEntry && S->HasCount && S->Count) { + IsUncoveredLine = false; + break; + } + + if (IsUncoveredLine) { + FirstUncoveredLineNo = LI.line_number(); + break; + } + } + } + if (ShowSourceName) - renderSourceName(OS, WholeFile); + renderSourceName(OS, WholeFile, FullyCovered, FirstUncoveredLineNo); renderTableHeader(OS, ViewDepth); // We need the expansions and instantiations sorted so we can go through them Index: tools/llvm-cov/SourceCoverageViewHTML.h =================================================================== --- tools/llvm-cov/SourceCoverageViewHTML.h +++ tools/llvm-cov/SourceCoverageViewHTML.h @@ -38,7 +38,8 @@ void renderViewFooter(raw_ostream &OS) override; - void renderSourceName(raw_ostream &OS, bool WholeFile) override; + void renderSourceName(raw_ostream &OS, bool WholeFile, bool FullyCovered, + unsigned LineNo) override; void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override; Index: tools/llvm-cov/SourceCoverageViewHTML.cpp =================================================================== --- tools/llvm-cov/SourceCoverageViewHTML.cpp +++ tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -338,7 +338,9 @@ OS << EndTable << EndCenteredDiv; } -void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) { +void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile, + bool FullyCovered, + unsigned LineNo) { OS << BeginSourceNameDiv; // Render the source name for the view. std::string SourceFile = isFunctionView() ? "Function: " : "Source: "; @@ -347,10 +349,16 @@ sys::path::remove_dots(SourceText, /*remove_dot_dots=*/true); sys::path::native(SourceText); OS << tag("pre", escape(SourceText, getOptions())); - // Render the object file name for the view. - if (WholeFile) + if (WholeFile) { + // Render the object file name for the view. OS << tag("pre", escape("Binary: " + getOptions().ObjectFilename, getOptions())); + // Render the "Go to first unexecuted line" link for the view. + std::string LinkText = escape("Go to first unexecuted line", getOptions()); + std::string LinkTarget = "#L" + utostr(uint64_t(LineNo)); + OS << (FullyCovered ? tag("pre", LinkText) + : tag("pre", a(LinkTarget, LinkText))); + } OS << EndSourceNameDiv; } Index: tools/llvm-cov/SourceCoverageViewText.h =================================================================== --- tools/llvm-cov/SourceCoverageViewText.h +++ tools/llvm-cov/SourceCoverageViewText.h @@ -38,7 +38,8 @@ void renderViewFooter(raw_ostream &OS) override; - void renderSourceName(raw_ostream &OS, bool WholeFile) override; + void renderSourceName(raw_ostream &OS, bool WholeFile, bool FullyCovered, + unsigned LineNo) override; void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override; Index: tools/llvm-cov/SourceCoverageViewText.cpp =================================================================== --- tools/llvm-cov/SourceCoverageViewText.cpp +++ tools/llvm-cov/SourceCoverageViewText.cpp @@ -63,7 +63,9 @@ void SourceCoverageViewText::renderViewFooter(raw_ostream &) {} -void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) { +void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile, + bool FullyCovered, + unsigned LineNo) { getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName() << ":\n"; if (WholeFile) {