Skip to content

Commit 7980273

Browse files
[SymbolGraphGen] Refactor export-import logic (swiftlang#61049) (swiftlang#61112)
rdar://99939379
1 parent d234cb3 commit 7980273

File tree

12 files changed

+89
-33
lines changed

12 files changed

+89
-33
lines changed

lib/SymbolGraphGen/SymbolGraph.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,10 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
321321

322322
// We are only interested in synthesized members that come from an
323323
// extension that we defined in our module.
324-
if (Info.EnablingExt && Info.EnablingExt->getModuleContext() != &M) {
325-
continue;
324+
if (Info.EnablingExt) {
325+
const auto *ExtM = Info.EnablingExt->getModuleContext();
326+
if (!Walker.isOurModule(ExtM))
327+
continue;
326328
}
327329

328330
for (const auto ExtensionMember : Info.Ext->getMembers()) {
@@ -398,7 +400,7 @@ void SymbolGraph::recordDefaultImplementationRelationships(Symbol S) {
398400
// If P is from a different module, and it's being added to a type
399401
// from the current module, add a `memberOf` relation to the extended
400402
// protocol.
401-
if (MemberVD->getModuleContext()->getNameStr() != M.getNameStr() && VD->getDeclContext()) {
403+
if (!Walker.isOurModule(MemberVD->getModuleContext()) && VD->getDeclContext()) {
402404
if (auto *ExP = VD->getDeclContext()->getSelfNominalTypeDecl()) {
403405
recordEdge(Symbol(this, VD, nullptr),
404406
Symbol(this, ExP, nullptr),

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@ namespace {
2525

2626
/// Compare the two \c ModuleDecl instances to see whether they are the same.
2727
///
28-
/// Pass \c true to the \c ignoreUnderlying argument to consider two modules the same even if
29-
/// one is a Swift module and the other a non-Swift module. This allows a Swift module and its
30-
/// underlying Clang module to compare as equal.
31-
bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs, bool ignoreUnderlying = false) {
32-
return lhs->getNameStr() == rhs->getNameStr()
33-
&& (ignoreUnderlying || lhs->isNonSwiftModule() == rhs->isNonSwiftModule());
28+
/// This does a by-name comparison to consider a module's underlying Clang module to be equivalent
29+
/// to the wrapping module of the same name.
30+
bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs) {
31+
return lhs->getNameStr() == rhs->getNameStr();
3432
}
3533

3634
} // anonymous namespace
@@ -50,11 +48,13 @@ SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M,
5048
SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
5149
auto *M = D->getModuleContext();
5250
const auto *DC = D->getDeclContext();
51+
SmallVector<const NominalTypeDecl *, 2> ParentTypes = {};
5352
const Decl *ExtendedNominal = nullptr;
5453
while (DC) {
5554
M = DC->getParentModule();
5655
if (const auto *NTD = dyn_cast_or_null<NominalTypeDecl>(DC->getAsDecl())) {
5756
DC = NTD->getDeclContext();
57+
ParentTypes.push_back(NTD);
5858
} else if (const auto *Ext = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl())) {
5959
DC = Ext->getExtendedNominal()->getDeclContext();
6060
if (!ExtendedNominal)
@@ -64,10 +64,10 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
6464
}
6565
}
6666

67-
if (areModulesEqual(&this->M, M, true)) {
67+
if (areModulesEqual(&this->M, M)) {
6868
return &MainGraph;
6969
} else if (MainGraph.DeclaringModule.hasValue() &&
70-
areModulesEqual(MainGraph.DeclaringModule.getValue(), M, true)) {
70+
areModulesEqual(MainGraph.DeclaringModule.getValue(), M)) {
7171
// Cross-import overlay modules already appear as "extensions" of their declaring module; we
7272
// should put actual extensions of that module into the main graph
7373
return &MainGraph;
@@ -79,9 +79,8 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
7979
return &MainGraph;
8080
}
8181

82-
if (ExtendedNominal && isFromExportedImportedModule(ExtendedNominal)) {
83-
return &MainGraph;
84-
} else if (!ExtendedNominal && isConsideredExportedImported(D)) {
82+
// If this type is the child of a type which was re-exported in a qualified export, use the main graph.
83+
if (llvm::any_of(ParentTypes, [&](const NominalTypeDecl *NTD){ return isQualifiedExportedImport(NTD); })) {
8584
return &MainGraph;
8685
}
8786

@@ -230,7 +229,7 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
230229
if (const auto *ExtendedNominal = Extension->getExtendedNominal()) {
231230
auto ExtendedModule = ExtendedNominal->getModuleContext();
232231
auto ExtendedSG = getModuleSymbolGraph(ExtendedNominal);
233-
if (ExtendedModule != &M) {
232+
if (!isOurModule(ExtendedModule)) {
234233
ExtendedSG->recordNode(Symbol(ExtendedSG, VD, nullptr));
235234
return true;
236235
}
@@ -244,22 +243,10 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
244243
}
245244

246245
bool SymbolGraphASTWalker::isConsideredExportedImported(const Decl *D) const {
247-
// First check the decl itself to see if it was directly re-exported.
248-
if (isFromExportedImportedModule(D))
249-
return true;
250-
251-
const auto *DC = D->getDeclContext();
252-
253-
// Next, see if the decl is a child symbol of another decl that was re-exported.
254-
if (DC) {
255-
if (const auto *VD = dyn_cast_or_null<ValueDecl>(DC->getAsDecl())) {
256-
if (isFromExportedImportedModule(VD))
257-
return true;
258-
}
259-
}
260-
261-
// Finally, check to see if this decl is an extension of something else that was re-exported.
246+
// Check to see if this decl is an extension of something else that was re-exported.
247+
// Do this first in case there's a chain of extensions that leads somewhere that's not a re-export.
262248
// FIXME: this considers synthesized members of extensions to be valid
249+
const auto *DC = D->getDeclContext();
263250
const Decl *ExtendedNominal = nullptr;
264251
while (DC && !ExtendedNominal) {
265252
if (const auto *ED = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl())) {
@@ -269,10 +256,23 @@ bool SymbolGraphASTWalker::isConsideredExportedImported(const Decl *D) const {
269256
}
270257
}
271258

272-
if (ExtendedNominal && isFromExportedImportedModule(ExtendedNominal)) {
259+
if (ExtendedNominal && isConsideredExportedImported(ExtendedNominal)) {
273260
return true;
274261
}
275262

263+
// Check to see if the decl is a child symbol of another decl that was re-exported.
264+
DC = D->getDeclContext();
265+
if (DC) {
266+
if (const auto *VD = dyn_cast_or_null<ValueDecl>(DC->getAsDecl())) {
267+
if (isConsideredExportedImported(VD))
268+
return true;
269+
}
270+
}
271+
272+
// Check the decl itself to see if it was directly re-exported.
273+
if (isFromExportedImportedModule(D) || isQualifiedExportedImport(D))
274+
return true;
275+
276276
// If none of the other checks passed, this wasn't from a re-export.
277277
return false;
278278
}
@@ -293,3 +293,7 @@ bool SymbolGraphASTWalker::isExportedImportedModule(const ModuleDecl *M) const {
293293
return areModulesEqual(M, MD->getModuleContext());
294294
});
295295
}
296+
297+
bool SymbolGraphASTWalker::isOurModule(const ModuleDecl *M) const {
298+
return areModulesEqual(M, &this->M) || isExportedImportedModule(M);
299+
}

lib/SymbolGraphGen/SymbolGraphASTWalker.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
4646

4747
/// The module that this symbol graph will represent.
4848
const ModuleDecl &M;
49-
49+
50+
// FIXME: these should be tracked per-graph, rather than at the top level
5051
const SmallPtrSet<ModuleDecl *, 4> ExportedImportedModules;
5152

5253
const llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> QualifiedExportedImports;
@@ -109,6 +110,9 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
109110

110111
/// Returns whether the given module is an `@_exported import` module.
111112
virtual bool isExportedImportedModule(const ModuleDecl *M) const;
113+
114+
/// Returns whether the given module is the main module, or is an `@_exported import` module.
115+
virtual bool isOurModule(const ModuleDecl *M) const;
112116
};
113117

114118
} // end namespace symbolgraphgen
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: cp -r %S/Inputs/ExportedImport/ObjcProperty.framework %t
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t/ObjcProperty.framework/Modules/ObjcProperty.swiftmodule/%target-swiftmodule-name -import-underlying-module -F %t -module-name ObjcProperty %S/Inputs/ExportedImport/A.swift
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module -o %t/ExportedImport.swiftmodule -F %t -module-name ExportedImport %s -emit-symbol-graph -emit-symbol-graph-dir %t
5+
// RUN: %FileCheck %s --input-file %t/ExportedImport.symbols.json
6+
7+
// REQUIRES: objc_interop
8+
9+
// CHECK-DAG: "precise":"s:So11ClangStructa12ObjcPropertyE05InnerB0V"
10+
// CHECK-DAG: "precise":"s:12ObjcProperty12SomeProtocolPAAE8someFuncyyF::SYNTHESIZED::s:So11ClangStructa12ObjcPropertyE05InnerB0V06NestedB0V",
11+
12+
@_exported import ObjcProperty
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
extension SwiftStruct {
2+
public struct InnerStruct {}
3+
}
4+
5+
extension SwiftStruct.InnerStruct {
6+
public struct NestedStruct {}
7+
}
8+
9+
public protocol SomeProtocol {}
10+
11+
extension SomeProtocol {
12+
public func someFunc() {}
13+
}
14+
15+
extension SwiftStruct.InnerStruct.NestedStruct: SomeProtocol {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
Name: ObjcProperty
3+
Typedefs:
4+
- Name: ClangStruct
5+
SwiftName: SwiftStruct
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
typedef struct {
2+
unsigned filler;
3+
} ClangStruct;

test/SymbolGraph/ClangImporter/Inputs/ExportedImport/ObjcProperty.framework/Modules/ObjcProperty.swiftmodule/.keep

Whitespace-only changes.

test/SymbolGraph/ClangImporter/Inputs/ExportedImport/ObjcProperty.framework/ObjcProperty

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
framework module ObjcProperty {
2+
header "ObjcProperty.h"
3+
export *
4+
}

test/SymbolGraph/Module/Inputs/ThirdOrder/B.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ import A
33
public extension SomeStruct {
44
struct InnerStruct: Equatable {}
55
}
6+
7+
public extension SomeStruct.InnerStruct {
8+
struct NestedStruct: Equatable {}
9+
}

test/SymbolGraph/Module/ThirdOrder.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@
1212
@_exported import B
1313

1414
// BASE-NOT: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::s:1A10SomeStructV1BE05InnerB0V"
15-
// EXT: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::s:1A10SomeStructV1BE05InnerB0V"
15+
// EXT-DAG: "s:SQsE2neoiySbx_xtFZ::SYNTHESIZED::s:1A10SomeStructV1BE05InnerB0V"
16+
17+
// BASE-NOT: "s:1A10SomeStructV1BE05InnerB0V06NestedB0V"
18+
// EXT-DAG: "s:1A10SomeStructV1BE05InnerB0V06NestedB0V"

0 commit comments

Comments
 (0)