Skip to content

Commit d6e49a9

Browse files
committed
[SymbolGraph] Put extending declarations in rootmost module
When extending another module's type in your module, serialize declarations in the extension into the other module's "extension" symbol graph file, including relationships. This mechanic should continue up to the rootmost module. For example: A.AStruct <- B.BStruct < C.CStruct Both BStruct and CStruct should go in `@A` symbol graph files because AStruct owns BStruct and by extension owns CStruct. This is reflected in documentation curation in some form already. rdar://60796811
1 parent 4264b39 commit d6e49a9

File tree

8 files changed

+155
-32
lines changed

8 files changed

+155
-32
lines changed

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

+17-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,20 @@ SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M,
2929

3030
/// Get a "sub" symbol graph for the parent module of a type that
3131
/// the main module `M` is extending.
32-
SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(ModuleDecl *M) {
32+
SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
33+
auto *M = D->getModuleContext();
34+
const auto *DC = D->getDeclContext();
35+
while (DC) {
36+
M = DC->getParentModule();
37+
if (const auto *NTD = dyn_cast_or_null<NominalTypeDecl>(DC->getAsDecl())) {
38+
DC = NTD->getDeclContext();
39+
} else if (const auto *Ext = dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl())) {
40+
DC = Ext->getExtendedNominal()->getDeclContext();
41+
} else {
42+
DC = nullptr;
43+
}
44+
}
45+
3346
if (this->M.getNameStr().equals(M->getNameStr())) {
3447
return &MainGraph;
3548
}
@@ -88,7 +101,7 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
88101
return true;
89102
}
90103

91-
auto SG = getModuleSymbolGraph(D->getModuleContext());
104+
auto SG = getModuleSymbolGraph(D);
92105

93106
// If this is an extension, let's check that it implies some new conformances,
94107
// potentially with generic requirements.
@@ -107,7 +120,7 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
107120
// grab them for some new conformsTo relationships.
108121
if (!Extension->getInherited().empty()) {
109122
auto ExtendedSG =
110-
getModuleSymbolGraph(ExtendedNominal->getModuleContext());
123+
getModuleSymbolGraph(ExtendedNominal);
111124

112125
// The symbol graph to use to record these relationships.
113126
SmallVector<const ProtocolDecl *, 4> Protocols;
@@ -175,7 +188,7 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
175188
= dyn_cast_or_null<ExtensionDecl>(VD->getDeclContext())) {
176189
if (const auto *ExtendedNominal = Extension->getExtendedNominal()) {
177190
auto ExtendedModule = ExtendedNominal->getModuleContext();
178-
auto ExtendedSG = getModuleSymbolGraph(ExtendedModule);
191+
auto ExtendedSG = getModuleSymbolGraph(ExtendedNominal);
179192
if (ExtendedModule != &M) {
180193
ExtendedSG->recordNode(Symbol(ExtendedSG, VD, nullptr));
181194
return true;

lib/SymbolGraphGen/SymbolGraphASTWalker.h

+23-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,29 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
6060

6161
// MARK: - Utilities
6262

63-
/// Get a "sub" symbol graph for the parent module of a type that the main module `M` is extending.
64-
SymbolGraph *getModuleSymbolGraph(ModuleDecl *M);
63+
/// Get a "sub" symbol graph for the appropriate module concerning a declaration.
64+
///
65+
/// This will get the "rootmost" module responsible for a declaration's
66+
/// documentation. For example:
67+
///
68+
/// Module A:
69+
///
70+
/// ```swift
71+
/// public struct AStruct {}
72+
/// ```
73+
///
74+
/// Module B:
75+
///
76+
/// ```swift
77+
/// import A
78+
/// extension AStruct {
79+
/// public struct BStruct {}
80+
/// }
81+
///
82+
/// `BStruct` will go in module A's extension symbol graph, because `BStruct`
83+
/// is a member of `AStruct`, and module A owns `AStruct`, and so on for
84+
/// further nestings.
85+
SymbolGraph *getModuleSymbolGraph(const Decl *D);
6586

6687
// MARK: - SourceEntityWalker
6788

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name BasicExtension -emit-module -emit-module-path %t/
3+
// RUN: %target-swift-symbolgraph-extract -module-name BasicExtension -I %t -pretty-print -output-dir %t
4+
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json
5+
6+
extension String {
7+
/// Return something.
8+
public var something: String {
9+
return "something"
10+
}
11+
}
12+
13+
// CHECK: module
14+
// CHECK-NEXT: "name": "BasicExtension"
15+
16+
// CHECK: "precise": "s:SS14BasicExtensionE9somethingSSvp"
17+
18+
// CHECK: "kind": "memberOf"
19+
// CHECK-NEXT: "source": "s:SS14BasicExtensionE9somethingSSvp"
20+
// CHECK-NEXT: "target": "s:SS"
21+
22+
// Extending `String` creates a memberOf relationship above.
23+
// However, it should not be included as a node because `String`
24+
// is owned by the Swift module.
25+
// rdar://58876107
26+
// CHECK-NOT: "precise": "s:SS"

test/SymbolGraph/Module/Extension.swift

-26
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public protocol P {
2+
associatedtype Thing
3+
func foo() -> Thing
4+
}
5+
6+
public struct AStruct<Thing>: P {
7+
public var thing: Thing
8+
public init(thing: Thing) {
9+
self.thing = thing
10+
}
11+
public func foo() -> Thing {
12+
return thing
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import A
2+
3+
extension AStruct {
4+
public struct BStruct {
5+
public func foo() -> Double {
6+
return 1.0
7+
}
8+
}
9+
}
10+
11+
extension AStruct.BStruct: P where Thing == Double {
12+
public func bar() {}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import A
2+
import B
3+
4+
extension AStruct.BStruct {
5+
public struct CStruct: P {
6+
public func foo() -> UInt8 {
7+
return 0
8+
}
9+
}
10+
}
11+
12+
extension AStruct.BStruct {
13+
public func baz() {}
14+
}
15+
16+
extension AStruct.BStruct.CStruct where Thing: Equatable {
17+
public func baz() {}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %S/Inputs/NestedExtensions/A.swift -I %t -module-name A -emit-module -emit-module-path %t/
3+
// RUN: %target-build-swift %S/Inputs/NestedExtensions/B.swift -I %t -module-name B -emit-module -emit-module-path %t/
4+
// RUN: %target-build-swift %s -module-name NestedExtensions -emit-module -I %t -emit-module-path %t/
5+
6+
// RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t
7+
// RUN: %target-swift-symbolgraph-extract -module-name B -I %t -pretty-print -output-dir %t
8+
// RUN: %target-swift-symbolgraph-extract -module-name NestedExtensions -I %t -pretty-print -output-dir %t
9+
10+
// RUN: %FileCheck %s --input-file %t/B.symbols.json --check-prefix=MODULEB
11+
// RUN: %FileCheck %s --input-file %t/B@A.symbols.json --check-prefix=MODULEBATA
12+
13+
// RUN: %FileCheck %s --input-file %t/NestedExtensions@A.symbols.json --check-prefix=NESTEDATA
14+
15+
// RUN: %FileCheck %s --input-file %t/NestedExtensions.symbols.json --check-prefix=NESTED
16+
17+
import A
18+
import B
19+
20+
extension AStruct.BStruct {
21+
public struct CStruct: P {
22+
public func foo() -> UInt8 {
23+
return 0
24+
}
25+
}
26+
}
27+
28+
extension AStruct.BStruct {
29+
public func baz() {}
30+
}
31+
32+
extension AStruct.BStruct.CStruct where Thing: Equatable {
33+
public func baz() {}
34+
}
35+
36+
// BStruct belongs to AStruct and so should only ever appear in B@A extension symbol graph files.
37+
// MODULEB-NOT: BStruct
38+
// MODULEBATA: "precise": "s:1A7AStructV1BE7BStructV"
39+
40+
// CStruct belongs to BStruct, and BStruct belongs to AStruct, so should only appear in NestedExtension@A.
41+
// NESTED-NOT: BStruct
42+
// NESTED-NOT: CStruct
43+
// NESTEDATB-NOT: BStruct
44+
// NESTEDATA: "precise": "s:1A7AStructV1BE7BStructV16NestedExtensionsE7CStructV"

0 commit comments

Comments
 (0)