Index: llvm/trunk/test/tools/llvm-cov/native_separators.c =================================================================== --- llvm/trunk/test/tools/llvm-cov/native_separators.c +++ llvm/trunk/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: llvm/trunk/test/tools/llvm-cov/showProjectSummary.cpp =================================================================== --- llvm/trunk/test/tools/llvm-cov/showProjectSummary.cpp +++ llvm/trunk/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: llvm/trunk/tools/llvm-cov/SourceCoverageView.h =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageView.h +++ llvm/trunk/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, + unsigned FirstUncoveredLineNo) = 0; /// \brief Render the line prefix at the given \p ViewDepth. virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0; Index: llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp +++ llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp @@ -82,6 +82,25 @@ 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; + // There is no uncovered line, return zero. + return 0; +} + std::string SourceCoverageView::formatCount(uint64_t N) { std::string Number = utostr(N); int Len = Number.size(); @@ -142,8 +161,12 @@ renderViewHeader(OS); + unsigned FirstUncoveredLineNo = 0; + if (WholeFile) + FirstUncoveredLineNo = getFirstUncoveredLineNo(); + if (ShowSourceName) - renderSourceName(OS, WholeFile); + renderSourceName(OS, WholeFile, FirstUncoveredLineNo); renderTableHeader(OS, ViewDepth); // We need the expansions and instantiations sorted so we can go through them Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.h =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.h +++ llvm/trunk/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, + unsigned FirstUncoveredLineNo) override; void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override; Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -338,7 +338,8 @@ OS << EndTable << EndCenteredDiv; } -void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) { +void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile, + unsigned FirstUncoveredLineNo) { OS << BeginSourceNameDiv; // Render the source name for the view. std::string SourceFile = isFunctionView() ? "Function: " : "Source: "; @@ -347,10 +348,18 @@ 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. + if (FirstUncoveredLineNo != 0) { // The file is not fully covered + std::string LinkText = + escape("Go to first unexecuted line", getOptions()); + std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo)); + OS << tag("pre", a(LinkTarget, LinkText)); + } + } OS << EndSourceNameDiv; } Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewText.h =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageViewText.h +++ llvm/trunk/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, + unsigned FirstUncoveredLineNo) override; void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override; Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewText.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageViewText.cpp +++ llvm/trunk/tools/llvm-cov/SourceCoverageViewText.cpp @@ -63,7 +63,8 @@ void SourceCoverageViewText::renderViewFooter(raw_ostream &) {} -void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) { +void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile, + unsigned FirstUncoveredLineNo) { getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName() << ":\n"; if (WholeFile) {