[clang] [Clang] Support includes translated to module imports in -header-include-filtering=direct-per-file (PR #156756)
Sina Mahdavi via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 22 11:13:14 PDT 2025
https://github.com/sina-mahdavi updated https://github.com/llvm/llvm-project/pull/156756
>From 8ab5647fc5c51b42bb67ca46a063fa9815ea46f4 Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sina_mahdavi at apple.com>
Date: Wed, 3 Sep 2025 14:17:27 -0700
Subject: [PATCH 1/6] [Clang] Support includes translated to module imports in
-header-include-filtering=direct-per-file
---
clang/lib/Frontend/HeaderIncludeGen.cpp | 73 ++++++++++++++++++++-----
1 file changed, 60 insertions(+), 13 deletions(-)
diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp
index 8ab335905f9f2..8de8d61b6262c 100644
--- a/clang/lib/Frontend/HeaderIncludeGen.cpp
+++ b/clang/lib/Frontend/HeaderIncludeGen.cpp
@@ -112,11 +112,17 @@ class HeaderIncludesJSONCallback : public PPCallbacks {
/// an array of separate entries, one for each non-system source file used in
/// the compilation showing only the direct includes and imports from that file.
class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
+ struct HeaderIncludeInfo {
+ SourceLocation location;
+ FileEntryRef file;
+ const Module *importedModule;
+ };
+
SourceManager &SM;
HeaderSearch &HSI;
raw_ostream *OutputFile;
bool OwnsOutputFile;
- using DependencyMap = llvm::DenseMap<FileEntryRef, SmallVector<FileEntryRef>>;
+ using DependencyMap = llvm::DenseMap<FileEntryRef, SmallVector<HeaderIncludeInfo>>;
DependencyMap Dependencies;
public:
@@ -390,18 +396,43 @@ void HeaderIncludesDirectPerFileCallback::EndOfMainFile() {
std::string Str;
llvm::raw_string_ostream OS(Str);
llvm::json::OStream JOS(OS);
- JOS.array([&] {
- for (auto S = SourceFiles.begin(), SE = SourceFiles.end(); S != SE; ++S) {
- JOS.object([&] {
- SmallVector<FileEntryRef> &Deps = Dependencies[*S];
- JOS.attribute("source", S->getName().str());
- JOS.attributeArray("includes", [&] {
- for (unsigned I = 0, N = Deps.size(); I != N; ++I)
- JOS.value(Deps[I].getName().str());
+ JOS.object([&] {
+ JOS.attribute("version", "2.0.0");
+ JOS.attributeArray("dependencies", [&] {
+ for (auto S = SourceFiles.begin(), SE = SourceFiles.end(); S != SE; ++S) {
+ JOS.object([&] {
+ SmallVector<HeaderIncludeInfo> &Deps = Dependencies[*S];
+ JOS.attribute("source", S->getName().str());
+ JOS.attributeArray("includes", [&] {
+ for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
+ if (!Deps[I].importedModule) {
+ JOS.object([&] {
+ PresumedLoc PLoc = SM.getPresumedLoc(Deps[I].location);
+ std::string locationStr = PLoc.isInvalid() ? "<invalid>" : std::to_string(PLoc.getLine()) + ":" + std::to_string(PLoc.getColumn());
+ JOS.attribute("location", locationStr);
+ JOS.attribute("file", Deps[I].file.getName());
+ });
+ }
+ }
+ });
+ JOS.attributeArray("imports", [&] {
+ for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
+ if (Deps[I].importedModule) {
+ JOS.object([&] {
+ PresumedLoc PLoc = SM.getPresumedLoc(Deps[I].location);
+ std::string locationStr = PLoc.isInvalid() ? "<invalid>" : std::to_string(PLoc.getLine()) + ":" + std::to_string(PLoc.getColumn());
+ JOS.attribute("location", locationStr);
+ JOS.attribute("module", Deps[I].importedModule->getTopLevelModuleName());
+ JOS.attribute("file", Deps[I].file.getName());
+ });
+ }
+ }
+ });
});
- });
- }
+ }
+ });
});
+
OS << "\n";
if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
@@ -427,7 +458,19 @@ void HeaderIncludesDirectPerFileCallback::InclusionDirective(
if (!FromFile)
return;
- Dependencies[*FromFile].push_back(*File);
+ FileEntryRef headerOrModule = *File;
+ if (ModuleImported && SuggestedModule) {
+ OptionalFileEntryRef ModuleMapFile = HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
+ if (ModuleMapFile) {
+ headerOrModule = *ModuleMapFile;
+ }
+ }
+
+ Dependencies[*FromFile].push_back({
+ .location = Loc,
+ .file = headerOrModule,
+ .importedModule = (ModuleImported ? SuggestedModule : nullptr)
+ });
}
void HeaderIncludesDirectPerFileCallback::moduleImport(SourceLocation ImportLoc,
@@ -448,5 +491,9 @@ void HeaderIncludesDirectPerFileCallback::moduleImport(SourceLocation ImportLoc,
if (!ModuleMapFile)
return;
- Dependencies[*FromFile].push_back(*ModuleMapFile);
+ Dependencies[*FromFile].push_back({
+ .location = Loc,
+ .file = *ModuleMapFile,
+ .importedModule = Imported
+ });
}
>From c4405abf5830ed6f858d1ec17817e24418c62e81 Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sina_mahdavi at apple.com>
Date: Thu, 4 Sep 2025 17:34:18 -0700
Subject: [PATCH 2/6] [Clang] Include source location file name in include
tracing output and fixing syntax
---
clang/lib/Frontend/HeaderIncludeGen.cpp | 51 +++++++++++--------------
1 file changed, 22 insertions(+), 29 deletions(-)
diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp
index 8de8d61b6262c..71047f95cfc2d 100644
--- a/clang/lib/Frontend/HeaderIncludeGen.cpp
+++ b/clang/lib/Frontend/HeaderIncludeGen.cpp
@@ -113,9 +113,12 @@ class HeaderIncludesJSONCallback : public PPCallbacks {
/// the compilation showing only the direct includes and imports from that file.
class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
struct HeaderIncludeInfo {
- SourceLocation location;
- FileEntryRef file;
- const Module *importedModule;
+ SourceLocation Location;
+ FileEntryRef File;
+ const Module *ImportedModule;
+
+ HeaderIncludeInfo(SourceLocation Location, FileEntryRef File, const Module *ImportedModule)
+ : Location(Location), File(File), ImportedModule(ImportedModule) {}
};
SourceManager &SM;
@@ -399,31 +402,27 @@ void HeaderIncludesDirectPerFileCallback::EndOfMainFile() {
JOS.object([&] {
JOS.attribute("version", "2.0.0");
JOS.attributeArray("dependencies", [&] {
- for (auto S = SourceFiles.begin(), SE = SourceFiles.end(); S != SE; ++S) {
+ for (const auto &S : SourceFiles) {
JOS.object([&] {
- SmallVector<HeaderIncludeInfo> &Deps = Dependencies[*S];
- JOS.attribute("source", S->getName().str());
+ SmallVector<HeaderIncludeInfo> &Deps = Dependencies[S];
+ JOS.attribute("source", S.getName().str());
JOS.attributeArray("includes", [&] {
for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
- if (!Deps[I].importedModule) {
+ if (!Deps[I].ImportedModule) {
JOS.object([&] {
- PresumedLoc PLoc = SM.getPresumedLoc(Deps[I].location);
- std::string locationStr = PLoc.isInvalid() ? "<invalid>" : std::to_string(PLoc.getLine()) + ":" + std::to_string(PLoc.getColumn());
- JOS.attribute("location", locationStr);
- JOS.attribute("file", Deps[I].file.getName());
+ JOS.attribute("location", Deps[I].Location.printToString(SM));
+ JOS.attribute("file", Deps[I].File.getName());
});
}
}
});
JOS.attributeArray("imports", [&] {
for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
- if (Deps[I].importedModule) {
+ if (Deps[I].ImportedModule) {
JOS.object([&] {
- PresumedLoc PLoc = SM.getPresumedLoc(Deps[I].location);
- std::string locationStr = PLoc.isInvalid() ? "<invalid>" : std::to_string(PLoc.getLine()) + ":" + std::to_string(PLoc.getColumn());
- JOS.attribute("location", locationStr);
- JOS.attribute("module", Deps[I].importedModule->getTopLevelModuleName());
- JOS.attribute("file", Deps[I].file.getName());
+ JOS.attribute("location", Deps[I].Location.printToString(SM));
+ JOS.attribute("module", Deps[I].ImportedModule->getTopLevelModuleName());
+ JOS.attribute("file", Deps[I].File.getName());
});
}
}
@@ -458,19 +457,16 @@ void HeaderIncludesDirectPerFileCallback::InclusionDirective(
if (!FromFile)
return;
- FileEntryRef headerOrModule = *File;
+ FileEntryRef HeaderOrModule = *File;
if (ModuleImported && SuggestedModule) {
OptionalFileEntryRef ModuleMapFile = HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
if (ModuleMapFile) {
- headerOrModule = *ModuleMapFile;
+ HeaderOrModule = *ModuleMapFile;
}
}
- Dependencies[*FromFile].push_back({
- .location = Loc,
- .file = headerOrModule,
- .importedModule = (ModuleImported ? SuggestedModule : nullptr)
- });
+ HeaderIncludeInfo DependenciesEntry(Loc, HeaderOrModule, (ModuleImported ? SuggestedModule : nullptr));
+ Dependencies[*FromFile].push_back(DependenciesEntry);
}
void HeaderIncludesDirectPerFileCallback::moduleImport(SourceLocation ImportLoc,
@@ -491,9 +487,6 @@ void HeaderIncludesDirectPerFileCallback::moduleImport(SourceLocation ImportLoc,
if (!ModuleMapFile)
return;
- Dependencies[*FromFile].push_back({
- .location = Loc,
- .file = *ModuleMapFile,
- .importedModule = Imported
- });
+ HeaderIncludeInfo DependenciesEntry(Loc, *ModuleMapFile, Imported);
+ Dependencies[*FromFile].push_back(DependenciesEntry);
}
>From e0c462e96da08702aed69d49ea07ba845ac2b415 Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sina_mahdavi at apple.com>
Date: Thu, 4 Sep 2025 19:18:51 -0700
Subject: [PATCH 3/6] [Clang] Add tests for
-header-include-filtering=direct-per-file v2 output
---
.../Preprocessor/Inputs/print-header-json/module.modulemap | 5 +++++
.../Inputs/print-header-json/system/module.modulemap | 4 ++++
clang/test/Preprocessor/print-header-json.c | 7 ++++++-
3 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Preprocessor/Inputs/print-header-json/module.modulemap
create mode 100644 clang/test/Preprocessor/Inputs/print-header-json/system/module.modulemap
diff --git a/clang/test/Preprocessor/Inputs/print-header-json/module.modulemap b/clang/test/Preprocessor/Inputs/print-header-json/module.modulemap
new file mode 100644
index 0000000000000..024c43d89e278
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/print-header-json/module.modulemap
@@ -0,0 +1,5 @@
+module module0 {
+ header "header0.h"
+ header "header1.h"
+ export *
+}
diff --git a/clang/test/Preprocessor/Inputs/print-header-json/system/module.modulemap b/clang/test/Preprocessor/Inputs/print-header-json/system/module.modulemap
new file mode 100644
index 0000000000000..8ed45ab4dcdbe
--- /dev/null
+++ b/clang/test/Preprocessor/Inputs/print-header-json/system/module.modulemap
@@ -0,0 +1,4 @@
+module systemmodule0 {
+ header "system2.h"
+ export *
+}
diff --git a/clang/test/Preprocessor/print-header-json.c b/clang/test/Preprocessor/print-header-json.c
index bb1830e5030d8..e9502f5512dcd 100644
--- a/clang/test/Preprocessor/print-header-json.c
+++ b/clang/test/Preprocessor/print-header-json.c
@@ -21,8 +21,13 @@
#include "header0.h"
#include "system2.h"
+// RUN: rm %t.txt
+// RUN: env CC_PRINT_HEADERS_FORMAT=json CC_PRINT_HEADERS_FILTERING=direct-per-file CC_PRINT_HEADERS_FILE=%t.txt %clang -fsyntax-only -I %S/Inputs/print-header-json -isystem %S/Inputs/print-header-json/system -fmodules -fimplicit-module-maps -fmodules-cache-path=%t %s -o /dev/null
+// RUN: cat %t.txt | FileCheck %s --check-prefix=SUPPORTED_PERFILE_MODULES
+
// SUPPORTED: {"source":"{{[^,]*}}print-header-json.c","includes":["{{[^,]*}}system0.h","{{[^,]*}}system3.h","{{[^,]*}}system2.h"]}
-// SUPPORTED_PERFILE: [{"source":"{{[^,]*}}print-header-json.c","includes":["{{[^,]*}}system0.h","{{[^,]*}}header0.h","{{[^,]*}}system2.h"]},{"source":"{{[^,]*}}header0.h","includes":["{{[^,]*}}system3.h","{{[^,]*}}header1.h","{{[^,]*}}header2.h"]}]
+// SUPPORTED_PERFILE: {"version":"2.0.0","dependencies":[{"source":"{{[^,]*}}print-header-json.c","includes":[{"location":"{{[^,]*}}print-header-json.c:20:1","file":"{{[^,]*}}system0.h"},{"location":"{{[^,]*}}print-header-json.c:21:1","file":"{{[^,]*}}header0.h"},{"location":"{{[^,]*}}print-header-json.c:22:1","file":"{{[^,]*}}system2.h"}],"imports":[]},{"source":"{{[^,]*}}header0.h","includes":[{"location":"{{[^,]*}}header0.h:1:1","file":"{{[^,]*}}system3.h"},{"location":"{{[^,]*}}header0.h:2:1","file":"{{[^,]*}}header1.h"},{"location":"{{[^,]*}}header0.h:3:1","file":"{{[^,]*}}header2.h"}],"imports":[]}]}
+// SUPPORTED_PERFILE_MODULES: {"version":"2.0.0","dependencies":[{"source":"{{[^,]*}}print-header-json.c","includes":[{"location":"{{[^,]*}}print-header-json.c:20:1","file":"{{[^,]*}}system0.h"}],"imports":[{"location":"{{[^,]*}}print-header-json.c:21:1","module":"module0","file":"{{[^,]*}}print-header-json/module.modulemap"},{"location":"{{[^,]*}}print-header-json.c:22:1","module":"systemmodule0","file":"{{[^,]*}}print-header-json/system/module.modulemap"}]}]}
// UNSUPPORTED0: error: unsupported combination: -header-include-format=textual and -header-include-filtering=only-direct-system
// UNSUPPORTED1: error: unsupported combination: -header-include-format=json and -header-include-filtering=none
>From e2dfe9e9c591a13b203fc2e8c1d0c2d70c54192a Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sinamhdv_apple at icloud.com>
Date: Mon, 15 Sep 2025 11:32:01 -0700
Subject: [PATCH 4/6] Rename HeaderOrModule in HeaderIncludeGen.cpp
Co-authored-by: Jan Svoboda <jan at svoboda.ai>
---
clang/lib/Frontend/HeaderIncludeGen.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp
index 71047f95cfc2d..8ce7a1a85454f 100644
--- a/clang/lib/Frontend/HeaderIncludeGen.cpp
+++ b/clang/lib/Frontend/HeaderIncludeGen.cpp
@@ -457,7 +457,7 @@ void HeaderIncludesDirectPerFileCallback::InclusionDirective(
if (!FromFile)
return;
- FileEntryRef HeaderOrModule = *File;
+ FileEntryRef HeaderOrModuleMapFile = *File;
if (ModuleImported && SuggestedModule) {
OptionalFileEntryRef ModuleMapFile = HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
if (ModuleMapFile) {
>From 6c92ecd715aabe752e95a50f77aeab8afb6c1663 Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sinamhdv_apple at icloud.com>
Date: Fri, 19 Sep 2025 15:44:45 -0700
Subject: [PATCH 5/6] Fix formatting and a compile error
---
clang/lib/Frontend/HeaderIncludeGen.cpp | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Frontend/HeaderIncludeGen.cpp b/clang/lib/Frontend/HeaderIncludeGen.cpp
index 8ce7a1a85454f..7cd9c8a3a5bd7 100644
--- a/clang/lib/Frontend/HeaderIncludeGen.cpp
+++ b/clang/lib/Frontend/HeaderIncludeGen.cpp
@@ -117,7 +117,8 @@ class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
FileEntryRef File;
const Module *ImportedModule;
- HeaderIncludeInfo(SourceLocation Location, FileEntryRef File, const Module *ImportedModule)
+ HeaderIncludeInfo(SourceLocation Location, FileEntryRef File,
+ const Module *ImportedModule)
: Location(Location), File(File), ImportedModule(ImportedModule) {}
};
@@ -125,7 +126,8 @@ class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
HeaderSearch &HSI;
raw_ostream *OutputFile;
bool OwnsOutputFile;
- using DependencyMap = llvm::DenseMap<FileEntryRef, SmallVector<HeaderIncludeInfo>>;
+ using DependencyMap =
+ llvm::DenseMap<FileEntryRef, SmallVector<HeaderIncludeInfo>>;
DependencyMap Dependencies;
public:
@@ -304,8 +306,8 @@ void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
}
}
-void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
- Token &FilenameTok,
+void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile,
+ const Token &FilenameTok,
SrcMgr::CharacteristicKind FileType) {
if (!DepOpts.ShowSkippedHeaderIncludes)
return;
@@ -421,7 +423,9 @@ void HeaderIncludesDirectPerFileCallback::EndOfMainFile() {
if (Deps[I].ImportedModule) {
JOS.object([&] {
JOS.attribute("location", Deps[I].Location.printToString(SM));
- JOS.attribute("module", Deps[I].ImportedModule->getTopLevelModuleName());
+ JOS.attribute(
+ "module",
+ Deps[I].ImportedModule->getTopLevelModuleName());
JOS.attribute("file", Deps[I].File.getName());
});
}
@@ -431,7 +435,7 @@ void HeaderIncludesDirectPerFileCallback::EndOfMainFile() {
}
});
});
-
+
OS << "\n";
if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
@@ -459,13 +463,15 @@ void HeaderIncludesDirectPerFileCallback::InclusionDirective(
FileEntryRef HeaderOrModuleMapFile = *File;
if (ModuleImported && SuggestedModule) {
- OptionalFileEntryRef ModuleMapFile = HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
+ OptionalFileEntryRef ModuleMapFile =
+ HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
if (ModuleMapFile) {
- HeaderOrModule = *ModuleMapFile;
+ HeaderOrModuleMapFile = *ModuleMapFile;
}
}
- HeaderIncludeInfo DependenciesEntry(Loc, HeaderOrModule, (ModuleImported ? SuggestedModule : nullptr));
+ HeaderIncludeInfo DependenciesEntry(
+ Loc, HeaderOrModuleMapFile, (ModuleImported ? SuggestedModule : nullptr));
Dependencies[*FromFile].push_back(DependenciesEntry);
}
>From abf11630ee53a191c322045aa31746ddc73a8ec5 Mon Sep 17 00:00:00 2001
From: Sina Mahdavi <sinamhdv_apple at icloud.com>
Date: Mon, 22 Sep 2025 00:59:48 -0700
Subject: [PATCH 6/6] Fix print-header-json tests failing on Windows because of
wrong path separator
---
clang/test/Preprocessor/print-header-json.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Preprocessor/print-header-json.c b/clang/test/Preprocessor/print-header-json.c
index e9502f5512dcd..057dcc27d8238 100644
--- a/clang/test/Preprocessor/print-header-json.c
+++ b/clang/test/Preprocessor/print-header-json.c
@@ -27,7 +27,7 @@
// SUPPORTED: {"source":"{{[^,]*}}print-header-json.c","includes":["{{[^,]*}}system0.h","{{[^,]*}}system3.h","{{[^,]*}}system2.h"]}
// SUPPORTED_PERFILE: {"version":"2.0.0","dependencies":[{"source":"{{[^,]*}}print-header-json.c","includes":[{"location":"{{[^,]*}}print-header-json.c:20:1","file":"{{[^,]*}}system0.h"},{"location":"{{[^,]*}}print-header-json.c:21:1","file":"{{[^,]*}}header0.h"},{"location":"{{[^,]*}}print-header-json.c:22:1","file":"{{[^,]*}}system2.h"}],"imports":[]},{"source":"{{[^,]*}}header0.h","includes":[{"location":"{{[^,]*}}header0.h:1:1","file":"{{[^,]*}}system3.h"},{"location":"{{[^,]*}}header0.h:2:1","file":"{{[^,]*}}header1.h"},{"location":"{{[^,]*}}header0.h:3:1","file":"{{[^,]*}}header2.h"}],"imports":[]}]}
-// SUPPORTED_PERFILE_MODULES: {"version":"2.0.0","dependencies":[{"source":"{{[^,]*}}print-header-json.c","includes":[{"location":"{{[^,]*}}print-header-json.c:20:1","file":"{{[^,]*}}system0.h"}],"imports":[{"location":"{{[^,]*}}print-header-json.c:21:1","module":"module0","file":"{{[^,]*}}print-header-json/module.modulemap"},{"location":"{{[^,]*}}print-header-json.c:22:1","module":"systemmodule0","file":"{{[^,]*}}print-header-json/system/module.modulemap"}]}]}
+// SUPPORTED_PERFILE_MODULES: {"version":"2.0.0","dependencies":[{"source":"{{[^,]*}}print-header-json.c","includes":[{"location":"{{[^,]*}}print-header-json.c:20:1","file":"{{[^,]*}}system0.h"}],"imports":[{"location":"{{[^,]*}}print-header-json.c:21:1","module":"module0","file":"{{[^,]*}}print-header-json{{[\/\\]+}}module.modulemap"},{"location":"{{[^,]*}}print-header-json.c:22:1","module":"systemmodule0","file":"{{[^,]*}}print-header-json{{[\/\\]+}}system{{[\/\\]+}}module.modulemap"}]}]}
// UNSUPPORTED0: error: unsupported combination: -header-include-format=textual and -header-include-filtering=only-direct-system
// UNSUPPORTED1: error: unsupported combination: -header-include-format=json and -header-include-filtering=none
More information about the cfe-commits
mailing list