Add flag for showing skipped headers in -H / --show-includes output

Consider the following set of files:

  a.cc:
  #include "a.h"

  a.h:
  #ifndef A_H
  #define A_H

  #include "b.h"
  #include "c.h"  // This gets "skipped".

  #endif

  b.h:
  #ifndef B_H
  #define B_H

  #include "c.h"

  #endif

  c.h:
  #ifndef C_H
  #define C_H

  void c();

  #endif

And the output of the -H option:

  $ clang -c -H a.cc
  . ./a.h
  .. ./b.h
  ... ./c.h

Note that the include of c.h in a.h is not shown in the output (GCC does the
same). This is because of the include guard optimization: clang knows c.h is
covered by an include guard which is already defined, so when it sees the
include in a.h, it skips it. The same would have happened if #pragma once were
used instead of include guards.

However, a.h *does* include c.h, and it may be useful to show that in the -H
output. This patch adds a flag for doing that.

Differential revision: https://reviews.llvm.org/D100480

GitOrigin-RevId: f29dcbdde10c86cfd89196fc2aa0e7f6ca3c9c4e
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 24bb3b8..9e15712 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -4945,6 +4945,11 @@
 def sys_header_deps : Flag<["-"], "sys-header-deps">,
   HelpText<"Include system headers in dependency output">,
   MarshallingInfoFlag<DependencyOutputOpts<"IncludeSystemHeaders">>;
+def show_skipped_includes : Flag<["-"], "show-skipped-includes">,
+  HelpText<"Show skipped includes in -H output.">,
+  DocBrief<[{#include files may be "skipped" due to include guard optimization
+             or #pragma once. This flag makes -H show also such includes.}]>,
+  MarshallingInfoFlag<DependencyOutputOpts<"ShowSkippedHeaderIncludes">>;
 def module_file_deps : Flag<["-"], "module-file-deps">,
   HelpText<"Include module files in dependency output">,
   MarshallingInfoFlag<DependencyOutputOpts<"IncludeModuleFiles">>;
diff --git a/include/clang/Frontend/DependencyOutputOptions.h b/include/clang/Frontend/DependencyOutputOptions.h
index 433e3ed..0c15193 100644
--- a/include/clang/Frontend/DependencyOutputOptions.h
+++ b/include/clang/Frontend/DependencyOutputOptions.h
@@ -39,6 +39,10 @@
                                      /// problems.
   unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list
   unsigned IncludeModuleFiles : 1; ///< Include module file dependencies.
+  unsigned ShowSkippedHeaderIncludes : 1; ///< With ShowHeaderIncludes, show
+                                          /// also includes that were skipped
+                                          /// due to the "include guard
+                                          /// optimization" or #pragma once.
 
   /// Destination of cl.exe style /showIncludes info.
   ShowIncludesDestination ShowIncludesDest = ShowIncludesDestination::None;
@@ -75,7 +79,8 @@
 public:
   DependencyOutputOptions()
       : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0),
-        AddMissingHeaderDeps(0), IncludeModuleFiles(0) {}
+        AddMissingHeaderDeps(0), IncludeModuleFiles(0),
+        ShowSkippedHeaderIncludes(0) {}
 };
 
 }  // end namespace clang
diff --git a/lib/Frontend/HeaderIncludeGen.cpp b/lib/Frontend/HeaderIncludeGen.cpp
index df3f534..1ee47d8 100644
--- a/lib/Frontend/HeaderIncludeGen.cpp
+++ b/lib/Frontend/HeaderIncludeGen.cpp
@@ -45,6 +45,9 @@
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
                    SrcMgr::CharacteristicKind FileType,
                    FileID PrevFID) override;
+
+  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
+                   SrcMgr::CharacteristicKind FileType) override;
 };
 }
 
@@ -181,3 +184,16 @@
                     MSStyle);
   }
 }
+
+void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
+                                         Token &FilenameTok,
+                                         SrcMgr::CharacteristicKind FileType) {
+  if (!DepOpts.ShowSkippedHeaderIncludes)
+    return;
+
+  if (!DepOpts.IncludeSystemHeaders && isSystem(FileType))
+    return;
+
+  PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
+                  CurrentIncludeDepth + 1, MSStyle);
+}
diff --git a/test/Frontend/Inputs/test.h b/test/Frontend/Inputs/test.h
index 98cc459..67fdacb 100644
--- a/test/Frontend/Inputs/test.h
+++ b/test/Frontend/Inputs/test.h
@@ -1 +1,7 @@
+#ifndef TEST_H
+#define TEST_H
+
 #include "test2.h"
+#include "test2.h"
+
+#endif
diff --git a/test/Frontend/Inputs/test2.h b/test/Frontend/Inputs/test2.h
index 6d1a0d4..92250dc 100644
--- a/test/Frontend/Inputs/test2.h
+++ b/test/Frontend/Inputs/test2.h
@@ -1 +1,6 @@
+#ifndef TEST2_H
+#define TEST2_H
+
 int x;
+
+#endif
diff --git a/test/Frontend/print-header-includes.c b/test/Frontend/print-header-includes.c
index c5b711a..c7f84e2 100644
--- a/test/Frontend/print-header-includes.c
+++ b/test/Frontend/print-header-includes.c
@@ -6,6 +6,16 @@
 // CHECK-NOT: . {{.*noline.h}}
 // CHECK: . {{.*test.h}}
 // CHECK: .. {{.*test2.h}}
+// CHECK-NOT: .. {{.*test2.h}}
+
+// RUN: %clang_cc1 -I%S -isystem %S/Inputs/SystemHeaderPrefix \
+// RUN:     -E -H -show-skipped-includes -o /dev/null %s 2> %t.stderr
+// RUN: FileCheck --check-prefix=SKIPPED < %t.stderr %s
+
+// SKIPPED-NOT: . {{.*noline.h}}
+// SKIPPED: . {{.*test.h}}
+// SKIPPED: .. {{.*test2.h}}
+// SKIPPED: .. {{.*test2.h}}
 
 // RUN: %clang_cc1 -I%S -include Inputs/test3.h -isystem %S/Inputs/SystemHeaderPrefix \
 // RUN:     -E -H -sys-header-deps -o /dev/null %s 2> %t.stderr