Skip to content

Commit 8e9c418

Browse files
auto-reexport inferred exported submodules
1 parent f156dff commit 8e9c418

File tree

6 files changed

+88
-10
lines changed

6 files changed

+88
-10
lines changed

include/swift/AST/Module.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ class ModuleDecl
810810
/// returns the module \c Foo.
811811
ModuleDecl *getTopLevelModule(bool overlay = false);
812812

813+
/// Returns whether or not this module is a submodule of the given module.
814+
/// If `this == M`, this returns false. If this is a submodule such as
815+
/// `Foo.Bar.Baz`, and the given module is either `Foo` or `Foo.Bar`, this
816+
/// returns true.
817+
bool isSubmoduleOf(const ModuleDecl *M) const;
818+
813819
bool isResilient() const {
814820
return getResilienceStrategy() != ResilienceStrategy::Default;
815821
}

lib/AST/Module.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,27 @@ ModuleDecl *ModuleDecl::getTopLevelModule(bool overlay) {
901901
return this;
902902
}
903903

904+
bool ModuleDecl::isSubmoduleOf(const ModuleDecl *M) const {
905+
// Swift modules don't currently support submodules.
906+
if (!isNonSwiftModule())
907+
return false;
908+
909+
auto *ClangParent = M->findUnderlyingClangModule();
910+
if (!ClangParent)
911+
return false;
912+
913+
auto *ClangModule = findUnderlyingClangModule();
914+
if (!ClangModule)
915+
return false;
916+
917+
while ((ClangModule = ClangModule->Parent)) {
918+
if (ClangModule == ClangParent)
919+
return true;
920+
}
921+
922+
return false;
923+
}
924+
904925
static bool isParsedModule(const ModuleDecl *mod) {
905926
// FIXME: If we ever get mixed modules that contain both SourceFiles and other
906927
// kinds of file units, this will break; there all callers of this function should
@@ -1435,8 +1456,12 @@ collectExportedImports(const ModuleDecl *topLevelModule,
14351456
stack.push_back(topLevelModule);
14361457
while (!stack.empty()) {
14371458
const ModuleDecl *module = stack.pop_back_val();
1438-
if (module->isNonSwiftModule())
1459+
if (module->isNonSwiftModule() && module != topLevelModule &&
1460+
!module->isSubmoduleOf(topLevelModule)) {
1461+
// Recurse into submodules of the top-level module so that we can
1462+
// re-export them if necessary.
14391463
continue;
1464+
}
14401465

14411466
for (const FileUnit *file : module->getFiles()) {
14421467
if (const SourceFile *source = dyn_cast<SourceFile>(file)) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3585,8 +3585,7 @@ class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer {
35853585
};
35863586
} // unnamed namespace
35873587

3588-
// FIXME(https://github.com/apple/swift-docc/issues/190): Should submodules still be crawled for the symbol graph?
3589-
bool ClangModuleUnit::shouldCollectDisplayDecls() const { return isTopLevel(); }
3588+
bool ClangModuleUnit::shouldCollectDisplayDecls() const { return true; }
35903589

35913590
void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
35923591
VectorDeclPtrConsumer consumer(results);

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ bool SymbolGraphASTWalker::isConsideredExportedImported(const Decl *D) const {
338338
}
339339

340340
bool SymbolGraphASTWalker::isFromExportedImportedModule(const Decl* D, bool countUnderlyingClangModule) const {
341-
auto *M = D->getModuleContext();
341+
auto *M = getRealModuleOf(D);
342342
return isQualifiedExportedImport(D) || isExportedImportedModule(M, countUnderlyingClangModule);
343343
}
344344

lib/SymbolGraphGen/SymbolGraphGen.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/Module.h"
1818
#include "swift/AST/NameLookup.h"
1919
#include "swift/Sema/IDETypeChecking.h"
20+
#include "clang/Basic/Module.h"
2021
#include "llvm/ADT/STLExtras.h"
2122
#include "llvm/Support/JSON.h"
2223
#include "llvm/Support/Path.h"
@@ -62,18 +63,34 @@ int symbolgraphgen::emitSymbolGraphForModule(
6263
ModuleDecl *M, const SymbolGraphOptions &Options) {
6364
ModuleDecl::ImportCollector importCollector(Options.MinimumAccessLevel);
6465

65-
auto importFilter = [&Options](const ModuleDecl *module) {
66-
if (!module)
67-
return false;
66+
bool IncludeClangSubmodules = false;
67+
if (auto *ClangModule = M->findUnderlyingClangModule()) {
68+
// If a Clang module has a definition that includes `export *; module * {
69+
// export *; }` then we want to treat those inferred submodules as part of
70+
// the parent module, to preserve historical behavior.
71+
if (ClangModule->InferSubmodules && ClangModule->InferExportWildcard) {
72+
IncludeClangSubmodules = true;
73+
}
74+
}
75+
76+
auto importFilter = [&Options, &IncludeClangSubmodules,
77+
&M](const ModuleDecl *module) {
78+
if (!module)
79+
return false;
80+
81+
if (IncludeClangSubmodules && module->isSubmoduleOf(M)) {
82+
return true;
83+
}
6884

85+
if (Options.AllowedReexportedModules.has_value())
6986
for (const auto &allowedModuleName : *Options.AllowedReexportedModules)
7087
if (allowedModuleName == module->getNameStr())
7188
return true;
7289

73-
return false;
74-
};
90+
return false;
91+
};
7592

76-
if (Options.AllowedReexportedModules.has_value())
93+
if (Options.AllowedReexportedModules.has_value() || IncludeClangSubmodules)
7794
importCollector.importFilter = std::move(importFilter);
7895

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

0 commit comments

Comments
 (0)