Skip to content

Commit ab26b8b

Browse files
add support to getTopLevelDecls for clang submodules (#76401)
rdar://126031510
1 parent b3275d9 commit ab26b8b

23 files changed

+680
-264
lines changed

include/swift/AST/ClangNode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class ClangNode {
9696
/// clang::ImportDecl or null if it's neither.
9797
const clang::Module *getClangModule() const;
9898

99+
/// Returns the owning clang module of this node, if it exists.
100+
const clang::Module *getOwningClangModule() const;
101+
99102
clang::SourceLocation getLocation() const;
100103
clang::SourceRange getSourceRange() const;
101104

include/swift/AST/FileUnit.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,6 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
195195
virtual Identifier
196196
getDiscriminatorForPrivateDecl(const Decl *D) const = 0;
197197

198-
virtual bool shouldCollectDisplayDecls() const { return true; }
199-
200198
/// Finds all top-level decls in this file.
201199
///
202200
/// This does a simple local lookup, not recursively looking through imports.

include/swift/AST/Module.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,12 @@ class ModuleDecl
856856
/// returns the module \c Foo.
857857
ModuleDecl *getTopLevelModule(bool overlay = false);
858858

859+
/// Returns whether or not this module is a submodule of the given module.
860+
/// If `this == M`, this returns false. If this is a submodule such as
861+
/// `Foo.Bar.Baz`, and the given module is either `Foo` or `Foo.Bar`, this
862+
/// returns true.
863+
bool isSubmoduleOf(const ModuleDecl *M) const;
864+
859865
bool isResilient() const {
860866
return getResilienceStrategy() != ResilienceStrategy::Default;
861867
}
@@ -1095,15 +1101,6 @@ class ModuleDecl
10951101
/// The order of the results is not guaranteed to be meaningful.
10961102
void getPrecedenceGroups(SmallVectorImpl<PrecedenceGroupDecl*> &Results) const;
10971103

1098-
/// Determines whether this module should be recursed into when calling
1099-
/// \c getDisplayDecls.
1100-
///
1101-
/// Some modules should not call \c getDisplayDecls, due to assertions
1102-
/// in their implementation. These are usually implicit imports that would be
1103-
/// recursed into for parsed modules. This function provides a guard against
1104-
/// recusing into modules that should not have decls collected.
1105-
bool shouldCollectDisplayDecls() const;
1106-
11071104
/// Finds all top-level decls that should be displayed to a client of this
11081105
/// module.
11091106
///

include/swift/ClangImporter/ClangModule.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ class ClangModuleUnit final : public LoadedFile {
9090
ObjCSelector selector,
9191
SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
9292

93-
virtual bool shouldCollectDisplayDecls() const override;
94-
9593
virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
9694

9795
virtual void getDisplayDecls(SmallVectorImpl<Decl*> &results, bool recursive = false) const override;

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2702,7 +2702,11 @@ static void addNamespaceMembers(Decl *decl,
27022702
const auto *declOwner = namespaceDecl->getOwningModule();
27032703
if (declOwner)
27042704
declOwner = declOwner->getTopLevelModule();
2705-
for (auto redecl : namespaceDecl->redecls()) {
2705+
auto Redecls = llvm::SmallVector<clang::NamespaceDecl *, 2>(namespaceDecl->redecls());
2706+
std::stable_sort(Redecls.begin(), Redecls.end(), [&](clang::NamespaceDecl *LHS, clang::NamespaceDecl *RHS) {
2707+
return LHS->getOwningModule()->Name < RHS->getOwningModule()->Name;
2708+
});
2709+
for (auto redecl : Redecls) {
27062710
// Skip namespace declarations that come from other top-level modules.
27072711
if (const auto *redeclOwner = redecl->getOwningModule()) {
27082712
if (declOwner && declOwner != redeclOwner->getTopLevelModule())

lib/AST/Decl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ const clang::Module *ClangNode::getClangModule() const {
131131
return nullptr;
132132
}
133133

134+
const clang::Module *ClangNode::getOwningClangModule() const {
135+
if (auto *M = getAsModule())
136+
return M;
137+
if (auto D = getAsDecl())
138+
return D->getOwningModule();
139+
if (auto MI = getAsModuleMacro())
140+
return MI->getOwningModule();
141+
return nullptr;
142+
}
143+
134144
void ClangNode::dump() const {
135145
if (auto D = getAsDecl())
136146
D->dump();

lib/AST/Module.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,22 @@ ModuleDecl *ModuleDecl::getTopLevelModule(bool overlay) {
957957
return this;
958958
}
959959

960+
bool ModuleDecl::isSubmoduleOf(const ModuleDecl *M) const {
961+
// Swift modules don't currently support submodules.
962+
if (!isNonSwiftModule())
963+
return false;
964+
965+
auto *ClangParent = M->findUnderlyingClangModule();
966+
if (!ClangParent)
967+
return false;
968+
969+
auto *ClangModule = findUnderlyingClangModule();
970+
if (!ClangModule)
971+
return false;
972+
973+
return ClangModule->isSubModuleOf(ClangParent);
974+
}
975+
960976
static bool isParsedModule(const ModuleDecl *mod) {
961977
// FIXME: If we ever get mixed modules that contain both SourceFiles and other
962978
// kinds of file units, this will break; there all callers of this function should
@@ -1239,14 +1255,6 @@ void SourceFile::lookupObjCMethods(
12391255
results.append(known->second.begin(), known->second.end());
12401256
}
12411257

1242-
bool ModuleDecl::shouldCollectDisplayDecls() const {
1243-
for (const FileUnit *file : getFiles()) {
1244-
if (!file->shouldCollectDisplayDecls())
1245-
return false;
1246-
}
1247-
return true;
1248-
}
1249-
12501258
void ModuleDecl::getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &Results) const {
12511259
FORWARD(getLocalTypeDecls, (Results));
12521260
}
@@ -1471,9 +1479,6 @@ void ModuleDecl::ImportCollector::collect(
14711479
const ImportedModule &importedModule) {
14721480
auto *module = importedModule.importedModule;
14731481

1474-
if (!module->shouldCollectDisplayDecls())
1475-
return;
1476-
14771482
if (importFilter && !importFilter(module))
14781483
return;
14791484

@@ -1494,11 +1499,17 @@ static void
14941499
collectExportedImports(const ModuleDecl *topLevelModule,
14951500
ModuleDecl::ImportCollector &importCollector) {
14961501
SmallVector<const ModuleDecl *> stack;
1502+
SmallPtrSet<const ModuleDecl *, 4> visited;
1503+
visited.insert(topLevelModule);
14971504
stack.push_back(topLevelModule);
14981505
while (!stack.empty()) {
14991506
const ModuleDecl *module = stack.pop_back_val();
1500-
if (module->isNonSwiftModule())
1507+
if (module->isNonSwiftModule() && module != topLevelModule &&
1508+
!module->isSubmoduleOf(topLevelModule)) {
1509+
// Recurse into submodules of the top-level module so that we can
1510+
// re-export them if necessary.
15011511
continue;
1512+
}
15021513

15031514
for (const FileUnit *file : module->getFiles()) {
15041515
if (const SourceFile *source = dyn_cast<SourceFile>(file)) {
@@ -1518,10 +1529,12 @@ collectExportedImports(const ModuleDecl *topLevelModule,
15181529
ModuleDecl::ImportFilterKind::Exported);
15191530
for (const auto &im : exportedImports) {
15201531
// Skip collecting the underlying clang module as we already have the relevant import.
1521-
if (module->isClangOverlayOf(im.importedModule))
1522-
continue;
1523-
importCollector.collect(im);
1524-
stack.push_back(im.importedModule);
1532+
if (!module->isClangOverlayOf(im.importedModule))
1533+
importCollector.collect(im);
1534+
if (!visited.contains(im.importedModule)) {
1535+
visited.insert(im.importedModule);
1536+
stack.push_back(im.importedModule);
1537+
}
15251538
}
15261539
}
15271540
}

lib/ClangImporter/ClangImporter.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,6 +3099,15 @@ static bool isDeclaredInModule(const ClangModuleUnit *ModuleFilter,
30993099
if (VD->getModuleContext()->getName().str() == CLANG_HEADER_MODULE_NAME) {
31003100
return true;
31013101
}
3102+
// Because the ClangModuleUnit saved as a decl context will be saved as the top-level module, but
3103+
// the ModuleFilter we're given might be a submodule (if a submodule was passed to
3104+
// getTopLevelDecls, for example), we should compare the underlying Clang modules to determine
3105+
// module membership.
3106+
if (auto ClangNode = VD->getClangNode()) {
3107+
if (auto *ClangModule = ClangNode.getOwningClangModule()) {
3108+
return ModuleFilter->getClangModule() == ClangModule;
3109+
}
3110+
}
31023111
auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext();
31033112
return ModuleFilter == ContainingUnit;
31043113
}
@@ -3271,7 +3280,7 @@ class FilteringDeclaredDeclConsumer : public swift::VisibleDeclConsumer {
32713280
FilteringDeclaredDeclConsumer(swift::VisibleDeclConsumer &consumer,
32723281
const ClangModuleUnit *CMU)
32733282
: NextConsumer(consumer), ModuleFilter(CMU) {
3274-
assert(CMU && CMU->isTopLevel() && "Only top-level modules supported");
3283+
assert(CMU);
32753284
}
32763285

32773286
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
@@ -3622,9 +3631,6 @@ class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer {
36223631
};
36233632
} // unnamed namespace
36243633

3625-
// FIXME(https://github.com/apple/swift-docc/issues/190): Should submodules still be crawled for the symbol graph?
3626-
bool ClangModuleUnit::shouldCollectDisplayDecls() const { return isTopLevel(); }
3627-
36283634
void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
36293635
VectorDeclPtrConsumer consumer(results);
36303636
FilteringDeclaredDeclConsumer filterConsumer(consumer, this);
@@ -3645,10 +3651,12 @@ void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
36453651

36463652
// Add the extensions produced by importing categories.
36473653
for (auto category : lookupTable->categories()) {
3648-
if (auto extension = cast_or_null<ExtensionDecl>(
3649-
owner.importDecl(category, owner.CurrentVersion,
3650-
/*UseCanonical*/false))) {
3651-
results.push_back(extension);
3654+
if (category->getOwningModule() == clangModule) {
3655+
if (auto extension = cast_or_null<ExtensionDecl>(
3656+
owner.importDecl(category, owner.CurrentVersion,
3657+
/*UseCanonical*/false))) {
3658+
results.push_back(extension);
3659+
}
36523660
}
36533661
}
36543662

@@ -3663,11 +3671,11 @@ void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
36633671
};
36643672
// Retrieve all of the globals that will be mapped to members.
36653673

3666-
// FIXME: Since we don't represent Clang submodules as Swift
3667-
// modules, we're getting everything.
36683674
llvm::SmallPtrSet<ExtensionDecl *, 8> knownExtensions;
36693675
for (auto entry : lookupTable->allGlobalsAsMembers()) {
36703676
auto decl = entry.get<clang::NamedDecl *>();
3677+
if (decl->getOwningModule() != clangModule) continue;
3678+
36713679
Decl *importedDecl = owner.importDecl(decl, owner.CurrentVersion);
36723680
if (!importedDecl) continue;
36733681

lib/IDE/ModuleInterfacePrinting.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,6 @@ void swift::ide::printModuleInterface(
446446
const PrintOptions &Options,
447447
const bool PrintSynthesizedExtensions) {
448448

449-
// Clang submodules aren't handled well by `getDisplayDecls()` (no decls are
450-
// returned), so map them to their top-level module and filter out the extra
451-
// results below.
452449
const clang::Module *TargetClangMod = TargetMod->findUnderlyingClangModule();
453450
ModuleDecl *TopLevelMod = TargetMod->getTopLevelModule();
454451
bool IsSubmodule = TargetMod != TopLevelMod;
@@ -460,8 +457,8 @@ void swift::ide::printModuleInterface(
460457
auto AdjustedOptions = Options;
461458
adjustPrintOptions(AdjustedOptions);
462459

463-
SmallVector<Decl *, 1> Decls;
464-
swift::getTopLevelDeclsForDisplay(TopLevelMod, Decls);
460+
SmallVector<ModuleDecl *, 1> ModuleList;
461+
ModuleList.push_back(TargetMod);
465462

466463
SmallVector<ImportDecl *, 1> ImportDecls;
467464
llvm::DenseSet<const clang::Module *> ClangModulesForImports;
@@ -485,6 +482,10 @@ void swift::ide::printModuleInterface(
485482

486483
ClangDecls.insert({ CM, {} });
487484

485+
if (CM != TargetClangMod)
486+
if (auto *OwningModule = Importer.getWrapperForModule(CM))
487+
ModuleList.push_back(OwningModule);
488+
488489
// If we're supposed to visit submodules, add them now.
489490
if (TraversalOptions & ModuleTraversal::VisitSubmodules) {
490491
for (clang::Module * submodule: CM->submodules()) {
@@ -499,6 +500,12 @@ void swift::ide::printModuleInterface(
499500
}
500501
}
501502

503+
SmallVector<Decl *, 1> Decls;
504+
505+
for (ModuleDecl *M : ModuleList) {
506+
swift::getTopLevelDeclsForDisplay(M, Decls);
507+
}
508+
502509
// Collect those submodules that are actually imported but have no import
503510
// decls in the module.
504511
llvm::SmallPtrSet<const clang::Module *, 16> NoImportSubModules;

lib/SymbolGraphGen/SymbolGraph.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/AST/DeclObjC.h"
14+
#include "clang/Basic/Module.h"
1415
#include "swift/AST/ASTContext.h"
1516
#include "swift/AST/Comment.h"
1617
#include "swift/AST/Decl.h"
@@ -624,14 +625,14 @@ void SymbolGraph::serialize(llvm::json::OStream &OS) {
624625
OS.attributeObject("module", [&](){
625626
if (DeclaringModule) {
626627
// A cross-import overlay can be considered part of its declaring module
627-
OS.attribute("name", (*DeclaringModule)->getNameStr());
628+
OS.attribute("name", getFullModuleName(*DeclaringModule));
628629
std::vector<StringRef> B;
629630
for (auto BModule : BystanderModules) {
630631
B.push_back(BModule.str());
631632
}
632633
OS.attribute("bystanders", B);
633634
} else {
634-
OS.attribute("name", M.getNameStr());
635+
OS.attribute("name", getFullModuleName(&M));
635636
}
636637
AttributeRAII Platform("platform", OS);
637638

@@ -896,7 +897,15 @@ bool SymbolGraph::canIncludeDeclAsNode(const Decl *D,
896897

897898
// If this decl isn't in this module or module that this module imported with `@_exported`, don't record it,
898899
// as it will appear elsewhere in its module's symbol graph.
899-
if (D->getModuleContext()->getName() != M.getName() && !Walker.isConsideredExportedImported(D)) {
900+
901+
// If a Clang decl was declared in a submodule, the Swift decl's context will still point to the
902+
// top-level module. Instead, we need to probe the owning module on the Clang side, which will
903+
// correctly point to the submodule.
904+
auto RealModuleName = (std::string)D->getModuleContext()->getName();
905+
if (auto *ClangDecl = D->getClangDecl())
906+
if (auto *ClangModule = ClangDecl->getOwningModule())
907+
RealModuleName = ClangModule->Name;
908+
if (RealModuleName != (std::string)M.getName() && !Walker.isConsideredExportedImported(D)) {
900909
return false;
901910
}
902911

0 commit comments

Comments
 (0)