Skip to content

Commit d043378

Browse files
[SymbolGraphGen] Refactor export-import logic (#61049)
rdar://98808363
1 parent 9a39b20 commit d043378

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
@@ -322,8 +322,10 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
322322

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

329331
for (const auto ExtensionMember : Info.Ext->getMembers()) {
@@ -399,7 +401,7 @@ void SymbolGraph::recordDefaultImplementationRelationships(Symbol S) {
399401
// If P is from a different module, and it's being added to a type
400402
// from the current module, add a `memberOf` relation to the extended
401403
// protocol.
402-
if (MemberVD->getModuleContext()->getNameStr() != M.getNameStr() && VD->getDeclContext()) {
404+
if (!Walker.isOurModule(MemberVD->getModuleContext()) && VD->getDeclContext()) {
403405
if (auto *ExP = VD->getDeclContext()->getSelfNominalTypeDecl()) {
404406
recordEdge(Symbol(this, VD, nullptr),
405407
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
}
@@ -257,22 +256,10 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
257256
}
258257

259258
bool SymbolGraphASTWalker::isConsideredExportedImported(const Decl *D) const {
260-
// First check the decl itself to see if it was directly re-exported.
261-
if (isFromExportedImportedModule(D))
262-
return true;
263-
264-
const auto *DC = D->getDeclContext();
265-
266-
// Next, see if the decl is a child symbol of another decl that was re-exported.
267-
if (DC) {
268-
if (const auto *VD = dyn_cast_or_null<ValueDecl>(DC->getAsDecl())) {
269-
if (isFromExportedImportedModule(VD))
270-
return true;
271-
}
272-
}
273-
274-
// Finally, check to see if this decl is an extension of something else that was re-exported.
259+
// Check to see if this decl is an extension of something else that was re-exported.
260+
// Do this first in case there's a chain of extensions that leads somewhere that's not a re-export.
275261
// FIXME: this considers synthesized members of extensions to be valid
262+
const auto *DC = D->getDeclContext();
276263
const Decl *ExtendedNominal = nullptr;
277264
while (DC && !ExtendedNominal) {
278265
if (const auto *ED = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl())) {
@@ -282,10 +269,23 @@ bool SymbolGraphASTWalker::isConsideredExportedImported(const Decl *D) const {
282269
}
283270
}
284271

285-
if (ExtendedNominal && isFromExportedImportedModule(ExtendedNominal)) {
272+
if (ExtendedNominal && isConsideredExportedImported(ExtendedNominal)) {
286273
return true;
287274
}
288275

276+
// Check to see if the decl is a child symbol of another decl that was re-exported.
277+
DC = D->getDeclContext();
278+
if (DC) {
279+
if (const auto *VD = dyn_cast_or_null<ValueDecl>(DC->getAsDecl())) {
280+
if (isConsideredExportedImported(VD))
281+
return true;
282+
}
283+
}
284+
285+
// Check the decl itself to see if it was directly re-exported.
286+
if (isFromExportedImportedModule(D) || isQualifiedExportedImport(D))
287+
return true;
288+
289289
// If none of the other checks passed, this wasn't from a re-export.
290290
return false;
291291
}
@@ -306,3 +306,7 @@ bool SymbolGraphASTWalker::isExportedImportedModule(const ModuleDecl *M) const {
306306
return areModulesEqual(M, MD->getModuleContext());
307307
});
308308
}
309+
310+
bool SymbolGraphASTWalker::isOurModule(const ModuleDecl *M) const {
311+
return areModulesEqual(M, &this->M) || isExportedImportedModule(M);
312+
}

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)