Skip to content

Commit fcf6e56

Browse files
Merge pull request #73391 from daniel-grumberg/cherry-picks/release/6.0/cffb637
🍒[6.0][SymbolGraphGen] Correctly handle exported imports in swift-symbolgraph-extract
2 parents 9745480 + 4e30422 commit fcf6e56

16 files changed

+270
-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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,10 @@ 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=">,
1577+
Flags<[NoDriverOption, SwiftSymbolGraphExtractOption]>,
1578+
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.">;
1579+
15761580
def skip_synthesized_members: Flag<[ "-" ], "skip-synthesized-members">,
15771581
Flags<[NoDriverOption, SwiftSymbolGraphExtractOption]>,
15781582
HelpText<"Skip members inherited through classes or default implementations">;

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;

include/swift/SymbolGraphGen/SymbolGraphOptions.h

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

1313
#include "llvm/TargetParser/Triple.h"
14+
#include "llvm/ADT/ArrayRef.h"
15+
1416
#include "swift/AST/AttrKind.h"
1517

1618
#ifndef SWIFT_SYMBOLGRAPHGEN_SYMBOLGRAPHOPTIONS_H
@@ -63,6 +65,10 @@ struct SymbolGraphOptions {
6365
/// but SourceKit should be able to load the information when pulling symbol
6466
/// information for individual queries.
6567
bool PrintPrivateStdlibSymbols = false;
68+
69+
/// If this has a value specifies an explicit allow list of reexported module
70+
/// names that should be included symbol graph.
71+
std::optional<llvm::ArrayRef<StringRef>> AllowedReexportedModules = {};
6672
};
6773

6874
} // end namespace symbolgraphgen

lib/AST/Module.cpp

Lines changed: 91 additions & 64 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,101 @@ 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
}
1562-
// FIXME: Should this do extra access control filtering?
1563-
FORWARD(getDisplayDecls, (Results));
1564+
}
15641565

1565-
#ifndef NDEBUG
1566+
void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results, bool Recursive) const {
15661567
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-
}
1568+
ImportCollector importCollector;
1569+
this->getDisplayDeclsRecursivelyAndImports(Results, importCollector);
1570+
} else {
1571+
// FIXME: Should this do extra access control filtering?
1572+
FORWARD(getDisplayDecls, (Results));
1573+
}
1574+
}
1575+
1576+
void ModuleDecl::getDisplayDeclsRecursivelyAndImports(
1577+
SmallVectorImpl<Decl *> &results, ImportCollector &importCollector) const {
1578+
this->getDisplayDecls(results, /*Recursive=*/false);
1579+
1580+
// Look up imports recursively.
1581+
collectExportedImports(this, importCollector);
1582+
for (const auto &QI : importCollector.qualifiedImports) {
1583+
auto Module = QI.getFirst();
1584+
if (importCollector.imports.contains(Module))
1585+
continue;
15761586

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

2396-
bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) {
2423+
bool ModuleDecl::isClangOverlayOf(ModuleDecl *potentialUnderlying) const {
23972424
return getUnderlyingModuleIfOverlay() == potentialUnderlying;
23982425
}
23992426

lib/DriverTool/swift_symbolgraph_extract_main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ int swift_symbolgraph_extract_main(ArrayRef<const char *> Args,
163163
}
164164
}
165165

166+
SmallVector<StringRef, 4> AllowedRexports;
167+
if (auto *A =
168+
ParsedArgs.getLastArg(OPT_experimental_allowed_reexported_modules)) {
169+
for (const auto *val : A->getValues())
170+
AllowedRexports.emplace_back(val);
171+
}
172+
166173
symbolgraphgen::SymbolGraphOptions Options;
167174
Options.OutputDir = OutputDir;
168175
Options.Target = Target;
@@ -175,6 +182,7 @@ int swift_symbolgraph_extract_main(ArrayRef<const char *> Args,
175182
Options.EmitExtensionBlockSymbols =
176183
ParsedArgs.hasFlag(OPT_emit_extension_block_symbols,
177184
OPT_omit_extension_block_symbols, /*default=*/false);
185+
Options.AllowedReexportedModules = AllowedRexports;
178186

179187
if (auto *A = ParsedArgs.getLastArg(OPT_minimum_access_level)) {
180188
Options.MinimumAccessLevel =

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) {

0 commit comments

Comments
 (0)