Skip to content

Commit 10818f5

Browse files
collect submodule symbols into the parent module if it has an umbrella header
1 parent 9daa5a5 commit 10818f5

File tree

3 files changed

+102
-13
lines changed

3 files changed

+102
-13
lines changed

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,39 @@ bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs, bool isClangE
4545
return true;
4646
}
4747

48+
bool clangModuleExports(const clang::Module *ClangParent, const clang::Module *CM) {
49+
if (!ClangParent || !CM) return false;
50+
if (ClangParent == CM) return true;
51+
52+
for (auto ClangExport : ClangParent->Exports) {
53+
auto *ExportedModule = ClangExport.getPointer();
54+
if (ClangExport.getInt()) {
55+
if (!ExportedModule && CM->isSubModuleOf(ClangParent)) {
56+
return true;
57+
} else if (ExportedModule && CM->isSubModuleOf(ExportedModule)) {
58+
return true;
59+
}
60+
}
61+
if (ExportedModule && clangModuleExports(ExportedModule, CM)) {
62+
return true;
63+
}
64+
}
65+
66+
if (ClangParent->Exports.empty() && !std::holds_alternative<std::monostate>(ClangParent->Umbrella) && CM->isSubModuleOf(ClangParent)) {
67+
// HACK: Some SDK modules use an 'umbrella header' in place of an 'export *' declaration.
68+
// This is not the same thing, and the submodules are not actually being exported from the
69+
// umbrella header, but we're not doing complete dependency tracking here. To provide a proper
70+
// view into the symbols of this module, treat this umbrella declaration as an 'export *'.
71+
return true;
72+
}
73+
74+
return false;
75+
}
76+
77+
bool underlyingClangModuleExports(const ModuleDecl *ParentModule, const ModuleDecl *M) {
78+
return clangModuleExports(ParentModule->findUnderlyingClangModule(), M->findUnderlyingClangModule());
79+
}
80+
4881
} // anonymous namespace
4982

5083
SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M,
@@ -94,26 +127,42 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
94127
}
95128
}
96129

97-
if (areModulesEqual(&this->M, M)) {
98-
return &MainGraph;
99-
} else if (MainGraph.DeclaringModule.has_value() &&
100-
areModulesEqual(MainGraph.DeclaringModule.value(), M)) {
101-
// Cross-import overlay modules already appear as "extensions" of their declaring module; we
102-
// should put actual extensions of that module into the main graph
103-
return &MainGraph;
104-
}
130+
auto moduleIsMainGraph = [&](const ModuleDecl *M) {
131+
if (areModulesEqual(&this->M, M)) {
132+
return true;
133+
} else if (MainGraph.DeclaringModule.has_value() &&
134+
areModulesEqual(MainGraph.DeclaringModule.value(), M)) {
135+
// Cross-import overlay modules already appear as "extensions" of their declaring module; we
136+
// should put actual extensions of that module into the main graph
137+
return true;
138+
}
139+
140+
// Check the module and decl separately since the extension could be from a different module
141+
// than the decl itself.
142+
if (isExportedImportedModule(M)) {
143+
return true;
144+
}
145+
146+
return false;
147+
};
105148

106-
// Check the module and decl separately since the extension could be from a different module
107-
// than the decl itself.
108-
if (isExportedImportedModule(M) || isQualifiedExportedImport(D)) {
149+
if (moduleIsMainGraph(M) || isQualifiedExportedImport(D))
109150
return &MainGraph;
110-
}
111151

112152
// If this type is the child of a type which was re-exported in a qualified export, use the main graph.
113153
if (llvm::any_of(ParentTypes, [&](const NominalTypeDecl *NTD){ return isQualifiedExportedImport(NTD); })) {
114154
return &MainGraph;
115155
}
116-
156+
157+
// As a shorthand when dealing with Clang submodules, use their top-level module's graph if the
158+
// submodule is ultimately exported from its top-level module.
159+
auto *TopLevelModule = M->getTopLevelModule();
160+
if (TopLevelModule != M && underlyingClangModuleExports(TopLevelModule, M))
161+
M = TopLevelModule;
162+
163+
if (moduleIsMainGraph(M))
164+
return &MainGraph;
165+
117166
auto Found = ExtendedModuleGraphs.find(M->getNameStr());
118167
if (Found != ExtendedModuleGraphs.end()) {
119168
return Found->getValue();

lib/SymbolGraphGen/SymbolGraphGen.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ int symbolgraphgen::emitSymbolGraphForModule(
8484
ExportedClangModules.insert(ClangExport.getPointer());
8585
}
8686
}
87+
88+
if (ExportedClangModules.empty() && WildcardExportClangModules.empty() && !std::holds_alternative<std::monostate>(ClangModule->Umbrella)) {
89+
// HACK: Some SDK modules use an 'umbrella header' in place of an 'export *' declaration.
90+
// This is not the same thing, and the submodules are not actually being exported from the
91+
// umbrella header, but we're not doing complete dependency tracking here. To provide a proper
92+
// view into the symbols of this module, treat this umbrella declaration as an 'export *'.
93+
WildcardExportClangModules.insert(ClangModule);
94+
}
8795
}
8896

8997
auto importFilter = [&Options, &WildcardExportClangModules,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: split-file %s %t
4+
5+
// RUN: %target-swift-symbolgraph-extract -sdk %clang-importer-sdk -module-name UmbrellaNoExport -I %t/UmbrellaNoExport -output-dir %t -pretty-print -v
6+
// RUN: %FileCheck %s --input-file %t/UmbrellaNoExport.symbols.json
7+
8+
// REQUIRES: objc_interop
9+
10+
//--- UmbrellaNoExport/module.modulemap
11+
module UmbrellaNoExport {
12+
umbrella header "UmbrellaNoExport.h"
13+
14+
module * {
15+
export *
16+
}
17+
}
18+
19+
//--- UmbrellaNoExport/UmbrellaNoExport.h
20+
#include "HeaderOne.h"
21+
#include "HeaderTwo.h"
22+
// CHECK-DAG: "precise": "c:UmbrellaNoExport.h@umbrellaVar"
23+
static int umbrellaVar = 0;
24+
25+
//--- UmbrellaNoExport/HeaderOne.h
26+
// CHECK-DAG: "precise": "c:HeaderOne.h@varOne"
27+
static int varOne = 1;
28+
29+
//--- UmbrellaNoExport/HeaderTwo.h
30+
// CHECK-DAG: "precise": "c:HeaderTwo.h@varTwo"
31+
static int varTwo = 2;
32+

0 commit comments

Comments
 (0)