Skip to content

Commit 4e56349

Browse files
authored
Merge pull request swiftlang#31032 from bitjammer/acgarland/rdar-61459287-no-members-for-defaultimplementations-of-requirements
[SymbolGraph] Don't emit memberOf for default implementations or requ…
2 parents 6998ad9 + 8316157 commit 4e56349

File tree

8 files changed

+108
-17
lines changed

8 files changed

+108
-17
lines changed

lib/SymbolGraphGen/SymbolGraph.cpp

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,50 @@ PrintOptions SymbolGraph::getDeclarationFragmentsPrintOptions() const {
6969
return Opts;
7070
}
7171

72+
bool
73+
SymbolGraph::isRequirementOrDefaultImplementation(const ValueDecl *VD) const {
74+
const auto *DC = VD->getDeclContext();
75+
76+
if (isa<ProtocolDecl>(DC) && VD->isProtocolRequirement()) {
77+
return true;
78+
}
79+
80+
// At this point, VD is either a default implementation of a requirement
81+
// or a freestanding implementation from a protocol extension without
82+
// a corresponding requirement.
83+
84+
auto *Proto = dyn_cast_or_null<ProtocolDecl>(DC->getSelfNominalTypeDecl());
85+
if (!Proto) {
86+
return false;
87+
}
88+
89+
/// Try to find a member of the owning protocol with the same name
90+
/// that is a requirement.
91+
auto FoundRequirementMemberNamed = [](DeclName Name,
92+
ProtocolDecl *Proto) -> bool {
93+
for (const auto *Member : Proto->lookupDirect(Name)) {
94+
if (isa<ProtocolDecl>(Member->getDeclContext()) &&
95+
Member->isProtocolRequirement()) {
96+
return true;
97+
}
98+
}
99+
return false;
100+
};
101+
102+
if (FoundRequirementMemberNamed(VD->getFullName(), Proto)) {
103+
return true;
104+
}
105+
for (auto *Inherited : Proto->getInheritedProtocols()) {
106+
if (FoundRequirementMemberNamed(VD->getFullName(), Inherited)) {
107+
return true;
108+
}
109+
}
110+
111+
// Couldn't find any requirement members of a protocol by this name.
112+
// It's not a requirement or default implementation of a requirement.
113+
return false;
114+
}
115+
72116
// MARK: - Symbols (Nodes)
73117

74118
void SymbolGraph::recordNode(Symbol S) {
@@ -102,15 +146,26 @@ void SymbolGraph::recordEdge(Symbol Source,
102146
}
103147

104148
void SymbolGraph::recordMemberRelationship(Symbol S) {
105-
auto *DC = S.getSymbolDecl()->getDeclContext();
149+
const auto *DC = S.getSymbolDecl()->getDeclContext();
106150
switch (DC->getContextKind()) {
107151
case DeclContextKind::GenericTypeDecl:
108152
case DeclContextKind::ExtensionDecl:
109153
case swift::DeclContextKind::EnumElementDecl:
154+
/*
155+
If this symbol is itself a protocol requirement, or
156+
is a default implementation of a protocol requirement,
157+
don't record a memberOf relationship.
158+
159+
This is to allow distinguishing between requirements,
160+
default implementations of requirements, and just other
161+
things added to protocols in extensions not related to their
162+
requirements.
163+
*/
164+
if (isRequirementOrDefaultImplementation(S.getSymbolDecl())) {
165+
return;
166+
}
110167
return recordEdge(S,
111-
Symbol(this,
112-
S.getSymbolDecl()->getDeclContext()->getSelfNominalTypeDecl(),
113-
nullptr),
168+
Symbol(this, DC->getSelfNominalTypeDecl(), nullptr),
114169
RelationshipKind::MemberOf());
115170
case swift::DeclContextKind::AbstractClosureExpr:
116171
case swift::DeclContextKind::Initializer:

lib/SymbolGraphGen/SymbolGraph.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ struct SymbolGraph {
202202
/// Returns `true` if the declaration should be included as a node
203203
/// in the graph.
204204
bool canIncludeDeclAsNode(const Decl *D) const;
205+
206+
/// Returns `true` if the declaration is a requirement of a protocol
207+
/// or is a default implementation of a protocol
208+
bool isRequirementOrDefaultImplementation(const ValueDecl *VD) const;
205209
};
206210

207211
} // end namespace symbolgraphgen

test/SymbolGraph/Relationships/DefaultImplementationOf/Basic.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ extension P {
1616
// CHECK: "kind": "defaultImplementationOf",
1717
// CHECK-NEXT: "source": "s:23DefaultImplementationOf1PPAAE1xSivp",
1818
// CHECK-NEXT: "target": "s:23DefaultImplementationOf1PP1xSivp"
19+
20+
// Since x is a default implementation of a requirement, we don't consider this to be a "member".
21+
// CHECK-NOT: memberOf

test/SymbolGraph/Relationships/DefaultImplementationOf/Indirect.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// RUN: %FileCheck %s --input-file %t/Indirect.symbols.json
55

66
public protocol P {
7-
associatedtype Thing
87
func foo()
98
}
109

@@ -15,3 +14,6 @@ extension Q {
1514
}
1615

1716
// CHECK-DAG: "kind": "defaultImplementationOf",{{[[:space:]]*}}"source": "s:8Indirect1QPAAE3fooyyF",{{[[:space:]]*}}"target": "s:8Indirect1PP3fooyyF"
17+
18+
// Since foo is a default implementation of a requirment, we don't consider this to be a "member"
19+
// CHECK-NOT: memberOf

test/SymbolGraph/Relationships/MemberOf.swift

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name Basic -emit-module -emit-module-path %t/
3+
// RUN: %target-swift-symbolgraph-extract -module-name Basic -I %t -pretty-print -output-dir %t
4+
// RUN: %FileCheck %s --input-file %t/Basic.symbols.json
5+
public struct S {
6+
public var x: Int
7+
}
8+
9+
// CHECK: "kind": "memberOf"
10+
// CHECK-NEXT: "source": "s:5Basic1SV1xSivp"
11+
// CHECK-NEXT: "target": "s:5Basic1SV"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name ProtocolMemberWithoutRequirement -emit-module -emit-module-path %t/
3+
// RUN: %target-swift-symbolgraph-extract -module-name ProtocolMemberWithoutRequirement -I %t -pretty-print -output-dir %t
4+
// RUN: %FileCheck %s --input-file %t/ProtocolMemberWithoutRequirement.symbols.json
5+
6+
public protocol P {}
7+
8+
extension P {
9+
public func foo() {}
10+
}
11+
public protocol Q : P {}
12+
13+
extension Q {
14+
public func bar() {}
15+
}
16+
17+
// foo is a member of P.
18+
// CHECK-DAG: "kind": "memberOf",{{[[:space:]]*}}"source": "s:32ProtocolMemberWithoutRequirement1PPAAE3fooyyF",{{[[:space:]]*}}"target": "s:32ProtocolMemberWithoutRequirement1PP"
19+
20+
// bar is a member of Q.
21+
// CHECK-DAG: "kind": "memberOf",{{[[:space:]]*}}"source": "s:32ProtocolMemberWithoutRequirement1QPAAE3baryyF",{{[[:space:]]*}}"target": "s:32ProtocolMemberWithoutRequirement1QP"
22+
23+
// foo is not a requirement of P nor a default implementation for any requirement.
24+
// Neither is bar for Q.
25+
// CHECK-NOT: requirementOf
26+
// CHECK-NOT: defaultImplementationOf
27+

test/SymbolGraph/Relationships/RequirementOf.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ public protocol P {
1010
// CHECK: "kind": "requirementOf"
1111
// CHECK-NEXT: "source": "s:10ConformsTo1PP1xSivp"
1212
// CHECK-NEXT: "target": "s:10ConformsTo1PP"
13+
// CHECK-NOT: defaultImplementationOf

0 commit comments

Comments
 (0)