Skip to content

Commit 9964884

Browse files
Recursively collect exported imports to allow fetching all visible Decls for symbol graph generation
This change is two fold. Firstly it enables collection of exported imports from non source file units. Additionally this recurses through the exported imports to ensure the transitive set is collected. Fixes #59920 rdar://89687175
1 parent f0bbbda commit 9964884

File tree

13 files changed

+222
-124
lines changed

13 files changed

+222
-124
lines changed

include/swift/AST/Module.h

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define SWIFT_MODULE_H
1919

2020
#include "swift/AST/AccessNotes.h"
21+
#include "swift/AST/AttrKind.h"
2122
#include "swift/AST/Decl.h"
2223
#include "swift/AST/DeclContext.h"
2324
#include "swift/AST/Identifier.h"
@@ -551,7 +552,7 @@ class ModuleDecl
551552
ModuleDecl *getUnderlyingModuleIfOverlay() const;
552553

553554
/// Returns true if this module is the Clang overlay of \p other.
554-
bool isClangOverlayOf(ModuleDecl *other);
555+
bool isClangOverlayOf(ModuleDecl *other) const;
555556

556557
/// Returns true if this module is the same module or either module is a clang
557558
/// overlay of the other.
@@ -1084,6 +1085,24 @@ class ModuleDecl
10841085
/// for that.
10851086
void getDisplayDecls(SmallVectorImpl<Decl*> &results, bool recursive = false) const;
10861087

1088+
struct ImportCollector {
1089+
SmallPtrSet<const ModuleDecl *, 4> imports;
1090+
llvm::SmallDenseMap<const ModuleDecl *, SmallPtrSet<Decl *, 4>, 4>
1091+
qualifiedImports;
1092+
AccessLevel minimumDocVisibility = AccessLevel::Private;
1093+
llvm::function_ref<bool(const ModuleDecl *)> importFilter = nullptr;
1094+
1095+
void collect(const ImportedModule &importedModule);
1096+
1097+
ImportCollector() = default;
1098+
ImportCollector(AccessLevel minimumDocVisibility)
1099+
: minimumDocVisibility(minimumDocVisibility) {}
1100+
};
1101+
1102+
void
1103+
getDisplayDeclsRecursivelyAndImports(SmallVectorImpl<Decl *> &results,
1104+
ImportCollector &importCollector) const;
1105+
10871106
using LinkLibraryCallback = llvm::function_ref<void(LinkLibrary)>;
10881107

10891108
/// Generate the list of libraries needed to link this module, based on its
@@ -1263,12 +1282,6 @@ inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) {
12631282
return extractNearestSourceLoc(static_cast<const Decl *>(mod));
12641283
}
12651284

1266-
/// Collects modules that this module imports via `@_exported import`.
1267-
void collectParsedExportedImports(const ModuleDecl *M,
1268-
SmallPtrSetImpl<ModuleDecl *> &Imports,
1269-
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports,
1270-
llvm::function_ref<bool(AttributedImport<ImportedModule>)> includeImport = nullptr);
1271-
12721285
/// If the import that would make the given declaration visibile is absent,
12731286
/// emit a diagnostic and a fix-it suggesting adding the missing import.
12741287
bool diagnoseMissingImportForMember(const ValueDecl *decl,

include/swift/Option/Options.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ def output_dir : Separate<["-"], "output-dir">,
15731573
HelpText<"Output directory">,
15741574
MetaVarName<"<dir>">;
15751575

1576-
def experimental_allowed_reexported_modules: CommaJoined<["-"], "experimental_allowed_reexported_modules">,
1576+
def experimental_allowed_reexported_modules: CommaJoined<["-"], "experimental-allowed-reexported-modules=">,
15771577
Flags<[NoDriverOption, SwiftSymbolGraphExtractOption]>,
15781578
HelpText<"Allow reexporting symbols from the provided modules if they are themselves exported from the main module. This is a comma separated list of module names.">;
15791579

include/swift/Sema/IDETypeChecking.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ namespace swift {
162162
void
163163
getTopLevelDeclsForDisplay(ModuleDecl *M, SmallVectorImpl<Decl*> &Results, bool Recursive = false);
164164

165+
/// Get all of the top-level declarations that should be printed as part of
166+
/// this module. This may force synthesis of top-level declarations that
167+
/// \p getDisplayDeclsForModule would only return if previous
168+
/// work happened to have synthesized them.
169+
void getTopLevelDeclsForDisplay(
170+
ModuleDecl *M, SmallVectorImpl<Decl *> &Results,
171+
llvm::function_ref<void(ModuleDecl *, SmallVectorImpl<Decl *> &)>
172+
getDisplayDeclsForModule);
173+
165174
struct ExtensionInfo {
166175
// The extension with the declarations to apply.
167176
ExtensionDecl *Ext;

lib/AST/Module.cpp

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/AST/ExistentialLayout.h"
2727
#include "swift/AST/FileUnit.h"
2828
#include "swift/AST/GenericEnvironment.h"
29+
#include "swift/AST/Import.h"
2930
#include "swift/AST/ImportCache.h"
3031
#include "swift/AST/LazyResolver.h"
3132
#include "swift/AST/LinkLibrary.h"
@@ -39,6 +40,7 @@
3940
#include "swift/AST/ProtocolConformance.h"
4041
#include "swift/AST/SourceFile.h"
4142
#include "swift/AST/SynthesizedFileUnit.h"
43+
#include "swift/AST/Type.h"
4244
#include "swift/AST/TypeCheckRequests.h"
4345
#include "swift/Basic/Compiler.h"
4446
#include "swift/Basic/SourceManager.h"
@@ -1287,42 +1289,6 @@ bool ModuleDecl::shouldCollectDisplayDecls() const {
12871289
return true;
12881290
}
12891291

1290-
void swift::collectParsedExportedImports(const ModuleDecl *M,
1291-
SmallPtrSetImpl<ModuleDecl *> &Imports,
1292-
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports,
1293-
llvm::function_ref<bool(AttributedImport<ImportedModule>)> includeImport) {
1294-
for (const FileUnit *file : M->getFiles()) {
1295-
if (const SourceFile *source = dyn_cast<SourceFile>(file)) {
1296-
if (source->hasImports()) {
1297-
for (auto import : source->getImports()) {
1298-
if (import.options.contains(ImportFlags::Exported) &&
1299-
(!includeImport || includeImport(import)) &&
1300-
import.module.importedModule->shouldCollectDisplayDecls()) {
1301-
auto *TheModule = import.module.importedModule;
1302-
1303-
if (import.module.getAccessPath().size() > 0) {
1304-
if (QualifiedImports.find(TheModule) == QualifiedImports.end()) {
1305-
QualifiedImports.try_emplace(TheModule);
1306-
}
1307-
auto collectDecls = [&](ValueDecl *VD,
1308-
DeclVisibilityKind reason) {
1309-
if (reason == DeclVisibilityKind::VisibleAtTopLevel)
1310-
QualifiedImports[TheModule].insert(VD);
1311-
};
1312-
auto consumer = makeDeclConsumer(std::move(collectDecls));
1313-
TheModule->lookupVisibleDecls(
1314-
import.module.getAccessPath(), consumer,
1315-
NLKind::UnqualifiedLookup);
1316-
} else if (!Imports.contains(TheModule)) {
1317-
Imports.insert(TheModule);
1318-
}
1319-
}
1320-
}
1321-
}
1322-
}
1323-
}
1324-
}
1325-
13261292
void ModuleDecl::getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &Results) const {
13271293
FORWARD(getLocalTypeDecls, (Results));
13281294
}
@@ -1543,40 +1509,100 @@ SourceFile::getExternalRawLocsForDecl(const Decl *D) const {
15431509
return Result;
15441510
}
15451511

1546-
void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results, bool Recursive) const {
1547-
if (Recursive && isParsedModule(this)) {
1548-
SmallPtrSet<ModuleDecl *, 4> Modules;
1549-
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> QualifiedImports;
1550-
collectParsedExportedImports(this, Modules, QualifiedImports);
1551-
for (const auto &QI : QualifiedImports) {
1552-
auto Module = QI.getFirst();
1553-
if (Modules.contains(Module)) continue;
1512+
void ModuleDecl::ImportCollector::collect(
1513+
const ImportedModule &importedModule) {
1514+
auto *module = importedModule.importedModule;
15541515

1555-
auto &Decls = QI.getSecond();
1556-
Results.append(Decls.begin(), Decls.end());
1557-
}
1558-
for (const ModuleDecl *import : Modules) {
1559-
import->getDisplayDecls(Results, Recursive);
1516+
if (!module->shouldCollectDisplayDecls())
1517+
return;
1518+
1519+
if (importFilter && !importFilter(module))
1520+
return;
1521+
1522+
if (importedModule.getAccessPath().size() > 0) {
1523+
auto collectDecls = [&](ValueDecl *VD, DeclVisibilityKind reason) {
1524+
if (reason == DeclVisibilityKind::VisibleAtTopLevel)
1525+
this->qualifiedImports[module].insert(VD);
1526+
};
1527+
auto consumer = makeDeclConsumer(std::move(collectDecls));
1528+
module->lookupVisibleDecls(importedModule.getAccessPath(), consumer,
1529+
NLKind::UnqualifiedLookup);
1530+
} else {
1531+
imports.insert(module);
1532+
}
1533+
}
1534+
1535+
static void
1536+
collectExportedImports(const ModuleDecl *module,
1537+
ModuleDecl::ImportCollector &importCollector) {
1538+
for (const FileUnit *file : module->getFiles()) {
1539+
if (const SourceFile *source = dyn_cast<SourceFile>(file)) {
1540+
if (source->hasImports()) {
1541+
for (const auto &import : source->getImports()) {
1542+
if (import.options.contains(ImportFlags::Exported) &&
1543+
import.docVisibility.value_or(AccessLevel::Public) >=
1544+
importCollector.minimumDocVisibility) {
1545+
importCollector.collect(import.module);
1546+
collectExportedImports(import.module.importedModule,
1547+
importCollector);
1548+
}
1549+
}
1550+
}
1551+
} else {
1552+
SmallVector<ImportedModule, 8> exportedImports;
1553+
file->getImportedModules(exportedImports,
1554+
ModuleDecl::ImportFilterKind::Exported);
1555+
for (const auto &im : exportedImports) {
1556+
// Skip collecting the underlying clang module as we already have the relevant import.
1557+
if (module->isClangOverlayOf(im.importedModule))
1558+
continue;
1559+
importCollector.collect(im);
1560+
collectExportedImports(im.importedModule, importCollector);
1561+
}
15601562
}
15611563
}
1564+
}
1565+
1566+
void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results, bool Recursive) const {
1567+
if (Recursive) {
1568+
ImportCollector importCollector;
1569+
this->getDisplayDeclsRecursivelyAndImports(Results, importCollector);
1570+
}
15621571
// FIXME: Should this do extra access control filtering?
15631572
FORWARD(getDisplayDecls, (Results));
1573+
}
15641574

1565-
#ifndef NDEBUG
1566-
if (Recursive) {
1567-
llvm::DenseSet<Decl *> visited;
1568-
for (auto *D : Results) {
1569-
// decls synthesized from implicit clang decls may appear multiple times;
1570-
// e.g. if multiple modules with underlying clang modules are re-exported.
1571-
// including duplicates of these is harmless, so skip them when counting
1572-
// this assertion
1573-
if (const auto *CD = D->getClangDecl()) {
1574-
if (CD->isImplicit()) continue;
1575-
}
1575+
void ModuleDecl::getDisplayDeclsRecursivelyAndImports(
1576+
SmallVectorImpl<Decl *> &results, ImportCollector &importCollector) const {
1577+
this->getDisplayDecls(results);
1578+
1579+
// Look up imports recursively.
1580+
collectExportedImports(this, importCollector);
1581+
for (const auto &QI : importCollector.qualifiedImports) {
1582+
auto Module = QI.getFirst();
1583+
if (importCollector.imports.contains(Module))
1584+
continue;
15761585

1577-
auto inserted = visited.insert(D).second;
1578-
assert(inserted && "there should be no duplicate decls");
1586+
auto &Decls = QI.getSecond();
1587+
results.append(Decls.begin(), Decls.end());
1588+
}
1589+
1590+
for (const ModuleDecl *import : importCollector.imports)
1591+
import->getDisplayDecls(results);
1592+
1593+
#ifndef NDEBUG
1594+
llvm::DenseSet<Decl *> visited;
1595+
for (auto *D : results) {
1596+
// decls synthesized from implicit clang decls may appear multiple times;
1597+
// e.g. if multiple modules with underlying clang modules are re-exported.
1598+
// including duplicates of these is harmless, so skip them when counting
1599+
// this assertion
1600+
if (const auto *CD = D->getClangDecl()) {
1601+
if (CD->isImplicit())
1602+
continue;
15791603
}
1604+
auto inserted = visited.insert(D).second;
1605+
assert(inserted && "there should be no duplicate decls");
15801606
}
15811607
#endif
15821608
}
@@ -2393,7 +2419,7 @@ ModuleDecl::getDeclaringModuleAndBystander() {
23932419
return *(declaringModuleAndBystander = {nullptr, Identifier()});
23942420
}
23952421

2396-
bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) {
2422+
bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) const {
23972423
return getUnderlyingModuleIfOverlay() == potentialUnderlying;
23982424
}
23992425

lib/IDE/IDETypeChecking.cpp

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift/Sema/IDETypeChecking.h"
14+
#include "swift/AST/ASTContext.h"
1315
#include "swift/AST/ASTDemangler.h"
1416
#include "swift/AST/ASTPrinter.h"
15-
#include "swift/AST/ASTContext.h"
1617
#include "swift/AST/Attr.h"
1718
#include "swift/AST/Decl.h"
1819
#include "swift/AST/Expr.h"
@@ -25,20 +26,30 @@
2526
#include "swift/AST/Requirement.h"
2627
#include "swift/AST/SourceFile.h"
2728
#include "swift/AST/Types.h"
28-
#include "swift/Sema/IDETypeChecking.h"
29-
#include "swift/Sema/IDETypeCheckingRequests.h"
30-
#include "swift/IDE/SourceEntityWalker.h"
3129
#include "swift/IDE/IDERequests.h"
30+
#include "swift/IDE/SourceEntityWalker.h"
3231
#include "swift/Parse/Lexer.h"
32+
#include "swift/Sema/IDETypeCheckingRequests.h"
33+
#include "llvm/ADT/SmallVector.h"
3334

3435
using namespace swift;
3536

36-
void
37-
swift::getTopLevelDeclsForDisplay(ModuleDecl *M,
38-
SmallVectorImpl<Decl*> &Results,
39-
bool Recursive) {
37+
void swift::getTopLevelDeclsForDisplay(ModuleDecl *M,
38+
SmallVectorImpl<Decl *> &Results,
39+
bool Recursive) {
40+
auto getDisplayDeclsForModule =
41+
[Recursive](ModuleDecl *M, SmallVectorImpl<Decl *> &Results) {
42+
M->getDisplayDecls(Results, Recursive);
43+
};
44+
getTopLevelDeclsForDisplay(M, Results, std::move(getDisplayDeclsForModule));
45+
}
46+
47+
void swift::getTopLevelDeclsForDisplay(
48+
ModuleDecl *M, SmallVectorImpl<Decl *> &Results,
49+
llvm::function_ref<void(ModuleDecl *, SmallVectorImpl<Decl *> &)>
50+
getDisplayDeclsForModule) {
4051
auto startingSize = Results.size();
41-
M->getDisplayDecls(Results, Recursive);
52+
getDisplayDeclsForModule(M, Results);
4253

4354
// Force Sendable on all public types, which might synthesize some extensions.
4455
// FIXME: We can remove this if @_nonSendable stops creating extensions.
@@ -48,20 +59,20 @@ swift::getTopLevelDeclsForDisplay(ModuleDecl *M,
4859
// Restrict this logic to public and package types. Non-public types
4960
// may refer to implementation details and fail at deserialization.
5061
auto accessScope = NTD->getFormalAccessScope();
51-
if (!M->isMainModule() &&
52-
!accessScope.isPublic() && !accessScope.isPackage())
62+
if (!M->isMainModule() && !accessScope.isPublic() &&
63+
!accessScope.isPackage())
5364
continue;
5465

5566
auto proto = M->getASTContext().getProtocol(KnownProtocolKind::Sendable);
5667
if (proto)
57-
(void) M->lookupConformance(NTD->getDeclaredInterfaceType(), proto);
68+
(void)M->lookupConformance(NTD->getDeclaredInterfaceType(), proto);
5869
}
5970
}
6071

6172
// Remove what we fetched and fetch again, possibly now with additional
6273
// extensions.
6374
Results.resize(startingSize);
64-
M->getDisplayDecls(Results, Recursive);
75+
getDisplayDeclsForModule(M, Results);
6576
}
6677

6778
static bool shouldPrintAsFavorable(const Decl *D, const PrintOptions &Options) {

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,14 @@ bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs, bool isClangE
4343

4444
} // anonymous namespace
4545

46+
SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M,
47+
const SymbolGraphOptions &Options)
48+
: Options(Options), M(M), MainGraph(*this, M, std::nullopt, Ctx) {}
49+
4650
SymbolGraphASTWalker::SymbolGraphASTWalker(
47-
ModuleDecl &M, const SmallPtrSet<ModuleDecl *, 4> ExportedImportedModules,
48-
const llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4>
51+
ModuleDecl &M,
52+
const SmallPtrSet<const ModuleDecl *, 4> ExportedImportedModules,
53+
const llvm::SmallDenseMap<const ModuleDecl *, SmallPtrSet<Decl *, 4>, 4>
4954
QualifiedExportedImports,
5055
const SymbolGraphOptions &Options)
5156
: Options(Options), M(M), ExportedImportedModules(ExportedImportedModules),

0 commit comments

Comments
 (0)