Index: llvm/trunk/test/tools/llvm-cov/zeroFunctionFile.c =================================================================== --- llvm/trunk/test/tools/llvm-cov/zeroFunctionFile.c +++ llvm/trunk/test/tools/llvm-cov/zeroFunctionFile.c @@ -10,10 +10,11 @@ // RUN: llvm-profdata merge %S/Inputs/zeroFunctionFile.proftext -o %t.profdata // RUN: llvm-cov report %S/Inputs/zeroFunctionFile.covmapping -instr-profile %t.profdata 2>&1 | FileCheck --check-prefix=REPORT --strict-whitespace %s -// REPORT: 0 0 - 0 0 - 0 0 - 0 0 - -// REPORT-NO: 0% +// REPORT: Files which contain no functions +// REPORT: zeroFunctionFile.h // RUN: llvm-cov show -j 1 %S/Inputs/zeroFunctionFile.covmapping -format html -instr-profile %t.profdata -o %t.dir // RUN: FileCheck %s -input-file=%t.dir/index.html -check-prefix=HTML -// HTML:
- (0/0)
 // HTML-NO: 0.00% (0/0)
+// HTML: Files which contain no functions
+// HTML: zeroFunctionFile.h
Index: llvm/trunk/tools/llvm-cov/CoverageReport.cpp
===================================================================
--- llvm/trunk/tools/llvm-cov/CoverageReport.cpp
+++ llvm/trunk/tools/llvm-cov/CoverageReport.cpp
@@ -371,8 +371,22 @@
   renderDivider(FileReportColumns, OS);
   OS << "\n";
 
-  for (const FileCoverageSummary &FCS : FileReports)
-    render(FCS, OS);
+  bool EmptyFiles = false;
+  for (const FileCoverageSummary &FCS : FileReports) {
+    if (FCS.FunctionCoverage.NumFunctions)
+      render(FCS, OS);
+    else
+      EmptyFiles = true;
+  }
+
+  if (EmptyFiles) {
+    OS << "\n"
+       << "Files which contain no functions:\n";
+
+    for (const FileCoverageSummary &FCS : FileReports)
+      if (!FCS.FunctionCoverage.NumFunctions)
+        render(FCS, OS);
+  }
 
   renderDivider(FileReportColumns, OS);
   OS << "\n";
Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.h
===================================================================
--- llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.h
+++ llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.h
@@ -38,6 +38,8 @@
   void emitFileSummary(raw_ostream &OS, StringRef SF,
                        const FileCoverageSummary &FCS,
                        bool IsTotals = false) const;
+  std::string buildLinkToFile(StringRef SF,
+                              const FileCoverageSummary &FCS) const;
 };
 
 /// \brief A code coverage view which supports html-based rendering.
Index: llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp
===================================================================
--- llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ llvm/trunk/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -294,6 +294,18 @@
   OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
 }
 
+std::string
+CoveragePrinterHTML::buildLinkToFile(StringRef SF,
+                                     const FileCoverageSummary &FCS) const {
+  SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
+  sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true);
+  sys::path::native(LinkTextStr);
+  std::string LinkText = escape(LinkTextStr, Opts);
+  std::string LinkTarget =
+      escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
+  return a(LinkTarget, LinkText);
+}
+
 /// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is
 /// false, link the summary to \p SF.
 void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
@@ -326,13 +338,7 @@
   if (IsTotals) {
     Filename = "TOTALS";
   } else {
-    SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
-    sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true);
-    sys::path::native(LinkTextStr);
-    std::string LinkText = escape(LinkTextStr, Opts);
-    std::string LinkTarget =
-        escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
-    Filename = a(LinkTarget, LinkText);
+    Filename = buildLinkToFile(SF, FCS);
   }
 
   Columns.emplace_back(tag("td", tag("pre", Filename)));
@@ -387,16 +393,39 @@
                         " for information about interpreting this report.");
 
   // Emit a table containing links to reports for each file in the covmapping.
+  // Exclude files which don't contain any regions.
   OSRef << BeginCenteredDiv << BeginTable;
   emitColumnLabelsForIndex(OSRef);
   FileCoverageSummary Totals("TOTALS");
   auto FileReports =
       CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles);
-  for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
-    emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
+  bool EmptyFiles = false;
+  for (unsigned I = 0, E = FileReports.size(); I < E; ++I) {
+    if (FileReports[I].FunctionCoverage.NumFunctions)
+      emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
+    else
+      EmptyFiles = true;
+  }
   emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
-  OSRef << EndTable << EndCenteredDiv
-        << tag("h5", escape(Opts.getLLVMVersionString(), Opts));
+  OSRef << EndTable << EndCenteredDiv;
+
+  // Emit links to files which don't contain any functions. These are normally
+  // not very useful, but could be relevant for code which abuses the
+  // preprocessor.
+  if (EmptyFiles) {
+    OSRef << tag("p", "Files which contain no functions. (These "
+                      "files contain code pulled into other files "
+                      "by the preprocessor.)\n");
+    OSRef << BeginCenteredDiv << BeginTable;
+    for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
+      if (!FileReports[I].FunctionCoverage.NumFunctions) {
+        std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]);
+        OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n';
+      }
+    OSRef << EndTable << EndCenteredDiv;
+  }
+
+  OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts));
   emitEpilog(OSRef);
 
   return Error::success();