Skip to content

Commit 4e85c51

Browse files
authored
Merge pull request #30857 from bitjammer/acgarland/rdar-60796811-rootmost-extension-sgf
[SymbolGraph] Put extending declarations in rootmost module
2 parents 3036774 + d6e49a9 commit 4e85c51

File tree

8 files changed

+155
-32
lines changed

8 files changed

+155
-32
lines changed

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 17 additions & 4 deletions
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

Lines changed: 23 additions & 2 deletions
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

Lines changed: 26 additions & 0 deletions
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/[email protected]
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

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
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+
}
Lines changed: 13 additions & 0 deletions
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+
}
Lines changed: 18 additions & 0 deletions
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+
}
Lines changed: 44 additions & 0 deletions
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/[email protected] --check-prefix=MODULEBATA
12+
13+
// RUN: %FileCheck %s --input-file %t/[email protected] --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)