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-NOT: >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 @@ -175,6 +175,9 @@ /// Specifies whether or not the view is a function view. bool FunctionView; + /// Get the first uncovered line number for the source file. + unsigned getFirstUncoveredLineNo(); + protected: struct LineRef { StringRef Line; @@ -195,7 +198,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 +293,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 @@ -82,6 +82,24 @@ llvm_unreachable("Unknown coverage output format!"); } +unsigned SourceCoverageView::getFirstUncoveredLineNo() { + auto CheckIfUncovered = [](const coverage::CoverageSegment S) { + return S.HasCount && S.Count == 0; + }; + // L is less than R if (1) it's an uncovered segment (has a 0 count), and (2) + // either R is not an uncovered segment, or L has a lower line number than R. + const auto MinSegIt = + std::min_element(CoverageInfo.begin(), CoverageInfo.end(), + [CheckIfUncovered](const coverage::CoverageSegment L, + const coverage::CoverageSegment R) { + return (CheckIfUncovered(L) && + (!CheckIfUncovered(R) || (L.Line < R.Line))); + }); + if (CheckIfUncovered(*MinSegIt)) + return (*MinSegIt).Line; + llvm_unreachable("This file isn't supposed to be fully covered"); +} + std::string SourceCoverageView::formatCount(uint64_t N) { std::string Number = utostr(N); int Len = Number.size(); @@ -136,14 +154,19 @@ } 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) + FirstUncoveredLineNo = getFirstUncoveredLineNo(); + 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)); + if (!FullyCovered) + OS << 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) {