@@ -45,6 +45,39 @@ bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs, bool isClangE
45
45
return true ;
46
46
}
47
47
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
+
48
81
} // anonymous namespace
49
82
50
83
SymbolGraphASTWalker::SymbolGraphASTWalker (ModuleDecl &M,
@@ -94,26 +127,42 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
94
127
}
95
128
}
96
129
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
+ };
105
148
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))
109
150
return &MainGraph;
110
- }
111
151
112
152
// If this type is the child of a type which was re-exported in a qualified export, use the main graph.
113
153
if (llvm::any_of (ParentTypes, [&](const NominalTypeDecl *NTD){ return isQualifiedExportedImport (NTD); })) {
114
154
return &MainGraph;
115
155
}
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
+
117
166
auto Found = ExtendedModuleGraphs.find (M->getNameStr ());
118
167
if (Found != ExtendedModuleGraphs.end ()) {
119
168
return Found->getValue ();
0 commit comments