Skip to content

[clang][scan-deps] Report a scanned TU's visible modules #147969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 11, 2025

Conversation

cyndyishida
Copy link
Member

@cyndyishida cyndyishida commented Jul 10, 2025

Clients of the dependency scanning service may need to add dependencies based on the visibility of importing modules, for example, when determining whether a Swift overlay dependency should be brought in based on whether there's a corresponding visible clang module for it.
This patch introduces a new field VisibleModules that contains all the visible top-level modules in a given TU.
Because visibility is determined by which headers or (sub)modules were imported, and not top-level module dependencies, the scanner now performs a separate DFS starting from what was directly imported for this computation.

In my local performance testing, there was no observable performance impact.

resolves: rdar://151416358

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Jul 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 10, 2025

@llvm/pr-subscribers-clang

Author: Cyndy Ishida (cyndyishida)

Changes

Clients of the dependency scanning service may need to add dependencies based on the visibility of top-level modules, for example, when determining whether a Swift overlay dependency should be brought in based on whether there's a corresponding visible clang module for it.
This patch introduces a new field VisibleModules that contains all the visible top-level modules in a given TU.
Because visibility is determined by which headers or (sub)modules were imported, and not top-level module dependencies, the scanner now performs a separate DFS starting from what was directly imported for this computation.

In my local performance testing, there was no observable performance impact.

resolves: rdar://151416358


Patch is 36.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/147969.diff

33 Files Affected:

  • (modified) clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h (+9)
  • (modified) clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h (+2)
  • (modified) clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h (+8)
  • (modified) clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (+2)
  • (modified) clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp (+30-1)
  • (modified) clang/test/ClangScanDeps/diagnostics.c (+3)
  • (modified) clang/test/ClangScanDeps/header-search-pruning-transitive.c (+8)
  • (modified) clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c (+9)
  • (modified) clang/test/ClangScanDeps/modules-context-hash-outputs.c (+6)
  • (modified) clang/test/ClangScanDeps/modules-context-hash-warnings.c (+6)
  • (modified) clang/test/ClangScanDeps/modules-context-hash.c (+6)
  • (modified) clang/test/ClangScanDeps/modules-dep-args.c (+4)
  • (modified) clang/test/ClangScanDeps/modules-extern-submodule.c (+4)
  • (modified) clang/test/ClangScanDeps/modules-extern-unrelated.m (+5)
  • (modified) clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m (+4)
  • (modified) clang/test/ClangScanDeps/modules-full-output-tu-order.c (+2)
  • (modified) clang/test/ClangScanDeps/modules-full.cpp (+13)
  • (modified) clang/test/ClangScanDeps/modules-has-include-umbrella-header.c (+6)
  • (modified) clang/test/ClangScanDeps/modules-implementation-private.m (+4)
  • (modified) clang/test/ClangScanDeps/modules-implicit-dot-private.m (+3)
  • (modified) clang/test/ClangScanDeps/modules-incomplete-umbrella.c (+8)
  • (modified) clang/test/ClangScanDeps/modules-inferred.m (+3)
  • (modified) clang/test/ClangScanDeps/modules-no-undeclared-includes.c (+3)
  • (modified) clang/test/ClangScanDeps/modules-pch-common-stale.c (+9)
  • (modified) clang/test/ClangScanDeps/modules-pch-common-submodule.c (+7)
  • (modified) clang/test/ClangScanDeps/modules-pch-common-via-submodule.c (+6)
  • (modified) clang/test/ClangScanDeps/modules-pch.c (+12)
  • (modified) clang/test/ClangScanDeps/modules-priv-fw-from-pub.m (+4)
  • (modified) clang/test/ClangScanDeps/multiple-commands.c (+14)
  • (modified) clang/test/ClangScanDeps/removed-args.c (+4)
  • (modified) clang/test/ClangScanDeps/tu-buffer.c (+4)
  • (added) clang/test/ClangScanDeps/visible-modules.c (+72)
  • (modified) clang/tools/clang-scan-deps/ClangScanDeps.cpp (+14)
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
index ee24e5d1543d3..14de81a5ff2b6 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -57,6 +57,10 @@ struct TranslationUnitDeps {
   /// determined that the differences are benign for this compilation.
   std::vector<ModuleID> ClangModuleDeps;
 
+  /// A list of module names that are visible to this translation unit. This
+  /// includes both direct and transitive module dependencies.
+  std::vector<std::string> VisibleModules;
+
   /// A list of the C++20 named modules this translation unit depends on.
   std::vector<std::string> NamedModuleDeps;
 
@@ -188,6 +192,10 @@ class FullDependencyConsumer : public DependencyConsumer {
     DirectModuleDeps.push_back(ID);
   }
 
+  void handleVisibleModule(std::string ModuleName) override {
+    VisibleModules.push_back(ModuleName);
+  }
+
   void handleContextHash(std::string Hash) override {
     ContextHash = std::move(Hash);
   }
@@ -210,6 +218,7 @@ class FullDependencyConsumer : public DependencyConsumer {
   std::string ModuleName;
   std::vector<std::string> NamedModuleDeps;
   std::vector<ModuleID> DirectModuleDeps;
+  std::vector<std::string> VisibleModules;
   std::vector<Command> Commands;
   std::string ContextHash;
   std::vector<std::string> OutputPaths;
diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
index 3e232c79397ce..6060e4b43312e 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -59,6 +59,8 @@ class DependencyConsumer {
 
   virtual void handleDirectModuleDependency(ModuleID MD) = 0;
 
+  virtual void handleVisibleModule(std::string ModuleName) = 0;
+
   virtual void handleContextHash(std::string Hash) = 0;
 };
 
diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
index e96c49883d3c6..4136cb73f7043 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -323,6 +323,11 @@ class ModuleDepCollector final : public DependencyCollector {
   llvm::MapVector<const Module *, PrebuiltModuleDep> DirectPrebuiltModularDeps;
   /// Working set of direct modular dependencies.
   llvm::SetVector<const Module *> DirectModularDeps;
+  /// Working set of direct modular dependencies, as they were imported.
+  llvm::SmallPtrSet<const Module *, 32> DirectImports;
+  /// All direct and transitive visible modules.
+  llvm::StringSet<> VisibleModules;
+
   /// Options that control the dependency output generation.
   std::unique_ptr<DependencyOutputOptions> Opts;
   /// A Clang invocation that's based on the original TU invocation and that has
@@ -337,6 +342,9 @@ class ModuleDepCollector final : public DependencyCollector {
   /// Checks whether the module is known as being prebuilt.
   bool isPrebuiltModule(const Module *M);
 
+  /// Computes all visible modules resolved from direct imports.
+  void addVisibleModules();
+
   /// Adds \p Path to \c FileDeps, making it absolute if necessary.
   void addFileDep(StringRef Path);
   /// Adds \p Path to \c MD.FileDeps, making it absolute if necessary.
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index 515211d47b348..d3d7641b83d48 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -40,6 +40,7 @@ class MakeDependencyPrinterConsumer : public DependencyConsumer {
   void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
   void handleModuleDependency(ModuleDeps MD) override {}
   void handleDirectModuleDependency(ModuleID ID) override {}
+  void handleVisibleModule(std::string ModuleName) override {}
   void handleContextHash(std::string Hash) override {}
 
   void printDependencies(std::string &S) {
@@ -175,6 +176,7 @@ TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
   TU.NamedModuleDeps = std::move(NamedModuleDeps);
   TU.FileDeps = std::move(Dependencies);
   TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
+  TU.VisibleModules = std::move(VisibleModules);
   TU.Commands = std::move(Commands);
 
   for (auto &&M : ClangModuleDeps) {
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index fa86d714ff69a..1a36171b0d483 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -673,8 +673,10 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
   if (MDC.isPrebuiltModule(TopLevelModule))
     MDC.DirectPrebuiltModularDeps.insert(
         {TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
-  else
+  else {
     MDC.DirectModularDeps.insert(TopLevelModule);
+    MDC.DirectImports.insert(Imported);
+  }
 }
 
 void ModuleDepCollectorPP::EndOfMainFile() {
@@ -706,6 +708,8 @@ void ModuleDepCollectorPP::EndOfMainFile() {
     if (!MDC.isPrebuiltModule(M))
       MDC.DirectModularDeps.insert(M);
 
+  MDC.addVisibleModules();
+
   for (const Module *M : MDC.DirectModularDeps)
     handleTopLevelModule(M);
 
@@ -727,6 +731,9 @@ void ModuleDepCollectorPP::EndOfMainFile() {
       MDC.Consumer.handleDirectModuleDependency(It->second->ID);
   }
 
+  for (auto &&I : MDC.VisibleModules)
+    MDC.Consumer.handleVisibleModule(std::string(I.getKey()));
+
   for (auto &&I : MDC.FileDeps)
     MDC.Consumer.handleFileDependency(I);
 
@@ -993,6 +1000,28 @@ bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
   return true;
 }
 
+void ModuleDepCollector::addVisibleModules() {
+  llvm::DenseSet<Module *> ImportedModules;
+  auto InsertVisibleModules = [&](const Module *M) {
+    if (ImportedModules.contains(M))
+      return;
+
+    VisibleModules.insert(M->getTopLevelModuleName());
+    SmallVector<Module *> Stack(M->Imports.begin(), M->Imports.end());
+    while (!Stack.empty()) {
+      Module *CurrModule = Stack.pop_back_val();
+      if (ImportedModules.contains(CurrModule))
+        continue;
+      ImportedModules.insert(CurrModule);
+      VisibleModules.insert(CurrModule->getTopLevelModuleName());
+      CurrModule->getExportedModules(Stack);
+    }
+  };
+
+  for (const Module *Import : DirectImports)
+    InsertVisibleModules(Import);
+}
+
 static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
                                           SmallVectorImpl<char> &Storage) {
   if (llvm::sys::path::is_absolute(Path) &&
diff --git a/clang/test/ClangScanDeps/diagnostics.c b/clang/test/ClangScanDeps/diagnostics.c
index 8e3cf4c9f9fa6..4d09740d7b5ae 100644
--- a/clang/test/ClangScanDeps/diagnostics.c
+++ b/clang/test/ClangScanDeps/diagnostics.c
@@ -50,6 +50,9 @@ module mod { header "mod.h" }
 // CHECK-NEXT:           "module-name": "mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-fimplicit-modules"
 // CHECK-NOT:          "-fimplicit-module-maps"
diff --git a/clang/test/ClangScanDeps/header-search-pruning-transitive.c b/clang/test/ClangScanDeps/header-search-pruning-transitive.c
index 1e829bb02ddc4..680228d51a181 100644
--- a/clang/test/ClangScanDeps/header-search-pruning-transitive.c
+++ b/clang/test/ClangScanDeps/header-search-pruning-transitive.c
@@ -104,6 +104,10 @@ module X { header "X.h" }
 // CHECK-NEXT:           "module-name": "X"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "X",
+// CHECK-NEXT:          "Y"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
 // CHECK:            "file-deps": [
@@ -159,6 +163,10 @@ module X { header "X.h" }
 // CHECK-NEXT:           "module-name": "X"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "X",
+// CHECK-NEXT:          "Y"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
 // CHECK:            "file-deps": [
diff --git a/clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c b/clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
index 9f7a62fb9eb74..065a10dfb4841 100644
--- a/clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
+++ b/clang/test/ClangScanDeps/modules-context-hash-ignore-macros.c
@@ -36,6 +36,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-DFOO"
 // CHECK-NOT:          "FOO"
@@ -49,6 +52,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:              "-D"
 // CHECK-NEXT:         "FOO"
@@ -62,6 +68,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:              "-fmodules-ignore-macro=FOO"
 // CHECK:              "-D"
diff --git a/clang/test/ClangScanDeps/modules-context-hash-outputs.c b/clang/test/ClangScanDeps/modules-context-hash-outputs.c
index 5e63e60a70370..75f8f1c583d92 100644
--- a/clang/test/ClangScanDeps/modules-context-hash-outputs.c
+++ b/clang/test/ClangScanDeps/modules-context-hash-outputs.c
@@ -34,6 +34,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:              "-dependency-file"
 // CHECK:            ]
@@ -46,6 +49,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-MF"
 // CHECK-NOT:          "-dependency-file"
diff --git a/clang/test/ClangScanDeps/modules-context-hash-warnings.c b/clang/test/ClangScanDeps/modules-context-hash-warnings.c
index 09d2f20b329e3..5a3d76e158c98 100644
--- a/clang/test/ClangScanDeps/modules-context-hash-warnings.c
+++ b/clang/test/ClangScanDeps/modules-context-hash-warnings.c
@@ -34,6 +34,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:              "-Wall"
 // CHECK:            ]
@@ -46,6 +49,9 @@
 // CHECK-NEXT:            "module-name": "Mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ]
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK-NOT:          "-Wall"
 // CHECK:            ]
diff --git a/clang/test/ClangScanDeps/modules-context-hash.c b/clang/test/ClangScanDeps/modules-context-hash.c
index 9489563576d3b..6ea831f7aed08 100644
--- a/clang/test/ClangScanDeps/modules-context-hash.c
+++ b/clang/test/ClangScanDeps/modules-context-hash.c
@@ -51,6 +51,9 @@
 // CHECK-NEXT:           "module-name": "mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:       "mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
 // CHECK:            "file-deps": [
@@ -89,6 +92,9 @@
 // CHECK:                "module-name": "mod"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:       "mod"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
 // CHECK:            "file-deps": [
diff --git a/clang/test/ClangScanDeps/modules-dep-args.c b/clang/test/ClangScanDeps/modules-dep-args.c
index 19f915923b84c..310a33e9533cf 100644
--- a/clang/test/ClangScanDeps/modules-dep-args.c
+++ b/clang/test/ClangScanDeps/modules-dep-args.c
@@ -87,6 +87,10 @@ module Direct { header "direct.h" }
 // CHECK-NEXT:           "module-name": "Direct"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "Direct",
+// CHECK-NEXT:          "Transitive"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK_CACHE:        "-fmodule-file={{.*}}/cache/{{.*}}/Direct-{{.*}}.pcm"
 // CHECK_BUILD:        "-fmodule-file={{.*}}/build/{{.*}}/Direct-{{.*}}.pcm"
diff --git a/clang/test/ClangScanDeps/modules-extern-submodule.c b/clang/test/ClangScanDeps/modules-extern-submodule.c
index 01d3d6ba5e0d3..7a2b49879b97b 100644
--- a/clang/test/ClangScanDeps/modules-extern-submodule.c
+++ b/clang/test/ClangScanDeps/modules-extern-submodule.c
@@ -107,6 +107,10 @@ module third {}
 // CHECK-NEXT:               "module-name": "first"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "first",
+// CHECK-NEXT:              "second"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK-NEXT:             "-cc1",
 // CHECK:                  "-fmodule-map-file=[[PREFIX]]/first/first/module.modulemap",
diff --git a/clang/test/ClangScanDeps/modules-extern-unrelated.m b/clang/test/ClangScanDeps/modules-extern-unrelated.m
index c003f0d9a2ee8..50ee7464419f7 100644
--- a/clang/test/ClangScanDeps/modules-extern-unrelated.m
+++ b/clang/test/ClangScanDeps/modules-extern-unrelated.m
@@ -113,6 +113,11 @@
 // CHECK-NEXT:               "module-name": "zeroth"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "first",
+// CHECK-NEXT:              "second",
+// CHECK-NEXT:              "zeroth"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK:                ],
 // CHECK-NEXT:           "executable": "{{.*}}",
diff --git a/clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m b/clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
index cfe29c2bf7cdb..0d91808e2bc04 100644
--- a/clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
+++ b/clang/test/ClangScanDeps/modules-fmodule-name-no-module-built.m
@@ -41,6 +41,10 @@
 // CHECK-NEXT:           "module-name": "header2"
 // CHECK-NEXT:         }
 // CHECK-NEXT:       ],
+// CHECK-NEXT:       "visible-clang-modules": [
+// CHECK-NEXT:          "header2",
+// CHECK-NEXT:          "header3"
+// CHECK-NEXT:       ],
 // CHECK-NEXT:       "command-line": [
 // CHECK:            ],
 // CHECK:            "file-deps": [
diff --git a/clang/test/ClangScanDeps/modules-full-output-tu-order.c b/clang/test/ClangScanDeps/modules-full-output-tu-order.c
index 065b8ac8ae07f..79e2947c5f039 100644
--- a/clang/test/ClangScanDeps/modules-full-output-tu-order.c
+++ b/clang/test/ClangScanDeps/modules-full-output-tu-order.c
@@ -31,6 +31,7 @@
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:           "clang-module-deps": [],
+// CHECK-NEXT:           "visible-clang-modules": [],
 // CHECK-NEXT:           "command-line": [
 // CHECK:                  "-D"
 // CHECK-NEXT:             "ONE"
@@ -47,6 +48,7 @@
 // CHECK-NEXT:         {
 // CHECK-NEXT:           "clang-context-hash": "{{.*}}",
 // CHECK-NEXT:           "clang-module-deps": [],
+// CHECK-NEXT:           "visible-clang-modules": [],
 // CHECK-NEXT:           "command-line": [
 // CHECK:                  "-D"
 // CHECK-NEXT:             "TWO"
diff --git a/clang/test/ClangScanDeps/modules-full.cpp b/clang/test/ClangScanDeps/modules-full.cpp
index 38db3af4403bb..67da82eb35960 100644
--- a/clang/test/ClangScanDeps/modules-full.cpp
+++ b/clang/test/ClangScanDeps/modules-full.cpp
@@ -97,6 +97,10 @@
 // CHECK-NEXT:               "module-name": "header1"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "header1",
+// CHECK-NEXT:              "header2"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK-NOT:              "-fimplicit-modules"
 // CHECK-NOT:              "-fimplicit-module-maps"
@@ -120,6 +124,9 @@
 // CHECK-NEXT:               "module-name": "header1"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "header1"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK-NOT:              "-fimplicit-modules"
 // CHECK-NOT:              "-fimplicit-module-maps"
@@ -143,6 +150,9 @@
 // CHECK-NEXT:               "module-name": "header1"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "header1"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK-NOT:              "-fimplicit-modules"
 // CHECK-NOT:              "-fimplicit-module-maps"
@@ -166,6 +176,9 @@
 // CHECK-NEXT:               "module-name": "header1"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:              "header1"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK-NOT:              "-fimplicit-modules"
 // CHECK-NOT:              "-fimplicit-module-maps"
diff --git a/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c b/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
index 022c59ca65db2..f27ea8e1ed2eb 100644
--- a/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
+++ b/clang/test/ClangScanDeps/modules-has-include-umbrella-header.c
@@ -62,6 +62,12 @@ module Dependency { header "dependency.h" }
 // CHECK-NEXT:               "module-name": "FW_Private"
 // CHECK-NEXT:             }
 // CHECK:                ],
+// CHECK:                "visible-clang-modules": [
+// CHECK-NEXT:              "Dependency",
+// CHECK-NEXT:              "FW_Private",
+// CHECK-NEXT:              "Import",
+// CHECK-NEXT:              "Poison"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK:                ],
 // CHECK:                "file-deps": [
diff --git a/clang/test/ClangScanDeps/modules-implementation-private.m b/clang/test/ClangScanDeps/modules-implementation-private.m
index b376073f4b9ee..59e77b85b7967 100644
--- a/clang/test/ClangScanDeps/modules-implementation-private.m
+++ b/clang/test/ClangScanDeps/modules-implementation-private.m
@@ -60,6 +60,10 @@
 // CHECK-NEXT:               "module-name": "FW_Private"
 // CHECK-NEXT:             }
 // CHECK-NEXT:           ],
+// CHECK-NEXT:           "visible-clang-modules": [
+// CHECK-NEXT:           "FW",
+// CHECK-NEXT:           "FW_Private"
+// CHECK-NEXT:           ],
 // CHECK-NEXT:           "command-line": [
 // CHECK:                ],
 // CHECK:                "file-deps": [
diff --git a/clang/test/ClangScanDeps/modules-implicit-dot-private.m b/clang/test/ClangScanDeps/modules-implicit-dot-private.m
index aa8caf3451dc4..5dfac2b09b316 100644
--- a/clang/test/ClangScanDeps/modules-implicit-dot-private.m
+++ b/clang/test/ClangScanDeps/modules-implicit-dot-private.m
@@ -75,6 +75,9 @@
 // CHECK-NEXT:           "module-name": ...
[truncated]

@cyndyishida
Copy link
Member Author

cyndyishida commented Jul 10, 2025

In my local performance testing, there was no observable performance impact.

To expand on what I tested, clang-scan-dep wall time for:

  • scanning a TU that includes every clang module under /S/L/F in the macOS 26 sdk.
  • scanning a real Objective-C-based project, that already took ~60 seconds to scan on my 10-core machine without my changes. There were 5222 compilation commands

All of these cases were tested 10 times with a clean & prepopulated module cache.

Copy link
Contributor

@jansvoboda11 jansvoboda11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this is not actually returning the set of (what Clang calls) visible modules. Can you clarify what the desired semantics actually is?

Copy link
Contributor

@jansvoboda11 jansvoboda11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cyndyishida cyndyishida merged commit 15c3793 into llvm:main Jul 11, 2025
9 checks passed
@cyndyishida cyndyishida deleted the eng/PR-151416358 branch July 11, 2025 16:33
cyndyishida added a commit to cyndyishida/llvm-project that referenced this pull request Jul 11, 2025
Clients of the dependency scanning service may need to add dependencies
based on the visibility of importing modules, for example, when
determining whether a Swift overlay dependency should be brought in
based on whether there's a corresponding **visible** clang module for
it.
This patch introduces a new field `VisibleModules` that contains all the
visible top-level modules in a given TU.
Because visibility is determined by which headers or (sub)modules were
imported, and not top-level module dependencies, the scanner now
performs a separate DFS starting from what was directly imported for
this computation.

In my local performance testing, there was no observable performance
impact.

resolves: rdar://151416358

---------

Co-authored-by: Jan Svoboda <[email protected]>
(cherry picked from commit 15c3793)
cyndyishida added a commit to cyndyishida/llvm-project that referenced this pull request Jul 11, 2025
Clients of the dependency scanning service may need to add dependencies
based on the visibility of importing modules, for example, when
determining whether a Swift overlay dependency should be brought in
based on whether there's a corresponding **visible** clang module for
it.
This patch introduces a new field `VisibleModules` that contains all the
visible top-level modules in a given TU.
Because visibility is determined by which headers or (sub)modules were
imported, and not top-level module dependencies, the scanner now
performs a separate DFS starting from what was directly imported for
this computation.

In my local performance testing, there was no observable performance
impact.

resolves: rdar://151416358

---------

Co-authored-by: Jan Svoboda <[email protected]>
(cherry picked from commit 15c3793)
artemcm added a commit to artemcm/swift that referenced this pull request Jul 12, 2025
…g modules only

Previously Swift overlay lookup was performed for every directly and transitively-imported Clang module.

llvm/llvm-project#147969 introduced the concept of "visible" Clang modules from a given named Clang dependency scanner query which closely maps to the set of modules for which Swift will attempt to load a Swift overlay. This change switches overlay querying to apply only to the set of such visible modules.

Resolves rdar://144797648
artemcm added a commit to artemcm/swift that referenced this pull request Jul 14, 2025
…g modules only

Previously Swift overlay lookup was performed for every directly and transitively-imported Clang module.

llvm/llvm-project#147969 introduced the concept of "visible" Clang modules from a given named Clang dependency scanner query which closely maps to the set of modules for which Swift will attempt to load a Swift overlay. This change switches overlay querying to apply only to the set of such visible modules.

Resolves rdar://144797648
artemcm added a commit to artemcm/swift that referenced this pull request Jul 14, 2025
…g modules only

Previously Swift overlay lookup was performed for every directly and transitively-imported Clang module.

llvm/llvm-project#147969 introduced the concept of "visible" Clang modules from a given named Clang dependency scanner query which closely maps to the set of modules for which Swift will attempt to load a Swift overlay. This change switches overlay querying to apply only to the set of such visible modules.

Resolves rdar://144797648
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants