Skip to content

Commit 3bd80e5

Browse files
authored
[index] Improvements on how conformances are recorded (#19043)
Query for the local conformances of a DeclContext and record conformances of functions that are defined in other types as 'implicit' overrides, with the type that introduces the conformance as container symbol. This allows more accurate index data, and avoids needing to query for all protocol requirements, which is expensive to calculate.
1 parent b3a3220 commit 3bd80e5

File tree

2 files changed

+206
-15
lines changed

2 files changed

+206
-15
lines changed

lib/Index/Index.cpp

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/Expr.h"
2020
#include "swift/AST/Module.h"
2121
#include "swift/AST/ParameterList.h"
22+
#include "swift/AST/ProtocolConformance.h"
2223
#include "swift/AST/Types.h"
2324
#include "swift/AST/USRGeneration.h"
2425
#include "swift/Basic/SourceManager.h"
@@ -78,6 +79,13 @@ static bool isMemberwiseInit(swift::ValueDecl *D) {
7879
return false;
7980
}
8081

82+
static SourceLoc getLocForExtension(ExtensionDecl *D) {
83+
// Use the 'End' token of the range, in case it is a compound name, e.g.
84+
// extension A.B {}
85+
// we want the location of 'B' token.
86+
return D->getExtendedTypeLoc().getSourceRange().End;
87+
}
88+
8189
namespace {
8290
// Adapter providing a common interface for a SourceFile/Module.
8391
class SourceFileOrModule {
@@ -121,6 +129,11 @@ class SourceFileOrModule {
121129
}
122130
};
123131

132+
struct ValueWitness {
133+
ValueDecl *Member;
134+
ValueDecl *Requirement;
135+
};
136+
124137
class IndexSwiftASTWalker : public SourceEntityWalker {
125138
IndexDataConsumer &IdxConsumer;
126139
SourceManager &SrcMgr;
@@ -133,6 +146,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
133146
Decl *D;
134147
SymbolInfo SymInfo;
135148
SymbolRoleSet Roles;
149+
SmallVector<ValueWitness, 6> ExplicitValueWitnesses;
136150
SmallVector<SourceLoc, 6> RefsToSuppress;
137151
};
138152
SmallVector<Entity, 6> EntitiesStack;
@@ -319,7 +333,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
319333
IndexSymbol Info;
320334
if (initIndexSymbol(Prop, *LabelIt++, /*IsRef=*/true, Info))
321335
continue;
322-
if (startEntity(Prop, Info))
336+
if (startEntity(Prop, Info, /*IsRef=*/true))
323337
finishCurrentEntity();
324338
}
325339
}
@@ -398,8 +412,10 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
398412
bool reportExtension(ExtensionDecl *D);
399413
bool reportRef(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info,
400414
Optional<AccessKind> AccKind);
415+
bool reportImplicitValueConformance(ValueDecl *witness, ValueDecl *requirement,
416+
Decl *container);
401417

402-
bool startEntity(Decl *D, IndexSymbol &Info);
418+
bool startEntity(Decl *D, IndexSymbol &Info, bool IsRef);
403419
bool startEntityDecl(ValueDecl *D);
404420

405421
bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related);
@@ -459,6 +475,14 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
459475
return true;
460476
}
461477

478+
/// Reports all implicit member value decl conformances that \p D introduces
479+
/// as implicit overrides at the source location of \p D, and returns the
480+
/// explicit ones so we can check against them later on when visiting them as
481+
/// members.
482+
///
483+
/// \returns false if AST visitation should stop.
484+
bool handleValueWitnesses(Decl *D, SmallVectorImpl<ValueWitness> &explicitValueWitnesses);
485+
462486
void getModuleHash(SourceFileOrModule SFOrMod, llvm::raw_ostream &OS);
463487
llvm::hash_code hashModule(llvm::hash_code code, SourceFileOrModule SFOrMod);
464488
llvm::hash_code hashFileReference(llvm::hash_code code,
@@ -612,16 +636,53 @@ bool IndexSwiftASTWalker::visitImports(
612636
return true;
613637
}
614638

615-
bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info) {
639+
bool IndexSwiftASTWalker::handleValueWitnesses(Decl *D, SmallVectorImpl<ValueWitness> &explicitValueWitnesses) {
640+
auto DC = dyn_cast<DeclContext>(D);
641+
if (!DC)
642+
return true;
643+
644+
for (auto *conf : DC->getLocalConformances()) {
645+
if (conf->isInvalid())
646+
continue;
647+
648+
auto normal = conf->getRootNormalConformance();
649+
normal->forEachValueWitness(nullptr,
650+
[&](ValueDecl *req, Witness witness) {
651+
if (Cancelled)
652+
return;
653+
654+
auto *decl = witness.getDecl();
655+
if (decl->getDeclContext() == DC) {
656+
explicitValueWitnesses.push_back(ValueWitness{decl, req});
657+
} else {
658+
// Report the implicit conformance.
659+
reportImplicitValueConformance(decl, req, D);
660+
}
661+
});
662+
}
663+
664+
if (Cancelled)
665+
return false;
666+
667+
return true;
668+
}
669+
670+
bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info, bool IsRef) {
616671
switch (IdxConsumer.startSourceEntity(Info)) {
617672
case swift::index::IndexDataConsumer::Abort:
618673
Cancelled = true;
619674
LLVM_FALLTHROUGH;
620675
case swift::index::IndexDataConsumer::Skip:
621676
return false;
622-
case swift::index::IndexDataConsumer::Continue:
623-
EntitiesStack.push_back({D, Info.symInfo, Info.roles, {}});
677+
case swift::index::IndexDataConsumer::Continue: {
678+
SmallVector<ValueWitness, 6> explicitValueWitnesses;
679+
if (!IsRef) {
680+
if (!handleValueWitnesses(D, explicitValueWitnesses))
681+
return false;
682+
}
683+
EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitValueWitnesses), {}});
624684
return true;
685+
}
625686
}
626687

627688
llvm_unreachable("Unhandled IndexDataConsumer in switch.");
@@ -649,12 +710,15 @@ bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) {
649710
return false;
650711
}
651712

652-
for (auto Overriden: getOverriddenDecls(D, /*IncludeProtocolReqs=*/!isSystemModule)) {
653-
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden))
654-
return false;
713+
for (auto Overriden: getOverriddenDecls(D, /*IncludeProtocolReqs=*/false)) {
714+
addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden);
655715
}
656716

657717
if (auto Parent = getParentDecl()) {
718+
for (const ValueWitness &witness : EntitiesStack.back().ExplicitValueWitnesses) {
719+
if (witness.Member == D)
720+
addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, witness.Requirement);
721+
}
658722
if (auto ParentVD = dyn_cast<ValueDecl>(Parent)) {
659723
SymbolRoleSet RelationsToParent = (SymbolRoleSet)SymbolRole::RelationChildOf;
660724
if (Info.symInfo.SubKind == SymbolSubKind::AccessorGetter ||
@@ -673,7 +737,7 @@ bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) {
673737
}
674738
}
675739

676-
return startEntity(D, Info);
740+
return startEntity(D, Info, /*IsRef=*/false);
677741
}
678742

679743
bool IndexSwiftASTWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit,
@@ -836,10 +900,7 @@ IndexSwiftASTWalker::getTypeLocAsNominalTypeDecl(const TypeLoc &Ty) {
836900
}
837901

838902
bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) {
839-
// Use the 'End' token of the range, in case it is a compound name, e.g.
840-
// extension A.B {}
841-
// we want the location of 'B' token.
842-
SourceLoc Loc = D->getExtendedTypeLoc().getSourceRange().End;
903+
SourceLoc Loc = getLocForExtension(D);
843904
NominalTypeDecl *NTD = D->getExtendedNominal();
844905
if (!NTD)
845906
return true;
@@ -850,7 +911,7 @@ bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) {
850911
if (initIndexSymbol(D, NTD, Loc, Info))
851912
return true;
852913

853-
if (!startEntity(D, Info))
914+
if (!startEntity(D, Info, /*IsRef=*/false))
854915
return false;
855916

856917
if (!reportRelatedRef(NTD, Loc, /*isImplicit=*/false,
@@ -940,7 +1001,7 @@ bool IndexSwiftASTWalker::reportRef(ValueDecl *D, SourceLoc Loc,
9401001
if (isSystemModule && !hasUsefulRoleInSystemModule(Info.roles))
9411002
return true;
9421003

943-
if (!startEntity(D, Info))
1004+
if (!startEntity(D, Info, /*IsRef=*/true))
9441005
return true;
9451006

9461007
// Report the accessors that were utilized.
@@ -961,6 +1022,34 @@ bool IndexSwiftASTWalker::reportRef(ValueDecl *D, SourceLoc Loc,
9611022
return finishCurrentEntity();
9621023
}
9631024

1025+
bool IndexSwiftASTWalker::reportImplicitValueConformance(ValueDecl *witness, ValueDecl *requirement,
1026+
Decl *container) {
1027+
if (!shouldIndex(witness, /*IsRef=*/true))
1028+
return true; // keep walking
1029+
1030+
SourceLoc loc;
1031+
if (auto *extD = dyn_cast<ExtensionDecl>(container))
1032+
loc = getLocForExtension(extD);
1033+
else
1034+
loc = container->getLoc();
1035+
1036+
IndexSymbol info;
1037+
if (initIndexSymbol(witness, loc, /*IsRef=*/true, info))
1038+
return true;
1039+
if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, requirement))
1040+
return true;
1041+
if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationContainedBy, container))
1042+
return true;
1043+
// Remove the 'ref' role that \c initIndexSymbol introduces. This isn't
1044+
// actually a 'reference', but an 'implicit' override.
1045+
info.roles &= ~(SymbolRoleSet)SymbolRole::Reference;
1046+
info.roles |= (SymbolRoleSet)SymbolRole::Implicit;
1047+
1048+
if (!startEntity(witness, info, /*IsRef=*/true))
1049+
return true;
1050+
return finishCurrentEntity();
1051+
}
1052+
9641053
bool IndexSwiftASTWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc,
9651054
bool IsRef, IndexSymbol &Info) {
9661055
assert(D);

test/Index/conformances.swift

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s
2+
3+
protocol P1 { // CHECK: [[@LINE]]:10 | protocol/Swift | P1 | [[P1_USR:.*]] | Def |
4+
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P1_foo_USR:.*]] | Def
5+
}
6+
7+
struct DirectConf: P1 { // CHECK: [[@LINE]]:8 | struct/Swift | DirectConf | [[DirectConf_USR:.*]] | Def
8+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[DirectConf_foo_USR:.*]] | Def,RelChild,RelOver | rel: 2
9+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
10+
// CHECK-NEXT: RelChild | struct/Swift | DirectConf | [[DirectConf_USR]]
11+
}
12+
13+
struct ConfFromExtension {}
14+
extension ConfFromExtension: P1 { // CHECK: [[@LINE]]:11 | extension/ext-struct/Swift | ConfFromExtension | [[ConfFromExtension_ext_USR:.*]] | Def
15+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[ConfFromExtension_ext_foo_USR:.*]] | Def,RelChild,RelOver | rel: 2
16+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
17+
// CHECK-NEXT: RelChild | extension/ext-struct/Swift | ConfFromExtension | [[ConfFromExtension_ext_USR]]
18+
}
19+
20+
struct ImplicitConfFromExtension { // CHECK: [[@LINE]]:8 | struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR:.*]] | Def
21+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[ImplicitConfFromExtension_foo_USR:.*]] | Def,RelChild | rel: 1
22+
// CHECK-NEXT: RelChild | struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR]]
23+
}
24+
extension ImplicitConfFromExtension: P1 { // CHECK: [[@LINE]]:11 | extension/ext-struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR:.*]] | Def
25+
// CHECK: [[@LINE-1]]:11 | instance-method/Swift | foo() | [[ImplicitConfFromExtension_foo_USR]] | Impl,RelOver,RelCont | rel: 2
26+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
27+
// CHECK-NEXT: RelCont | extension/ext-struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR]]
28+
}
29+
30+
class BaseConfFromBase { // CHECK: [[@LINE]]:7 | class/Swift | BaseConfFromBase | [[BaseConfFromBase_USR:.*]] | Def
31+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[BaseConfFromBase_foo_USR:.*]] | Def,Dyn,RelChild | rel: 1
32+
// CHECK-NEXT: RelChild | class/Swift | BaseConfFromBase | [[BaseConfFromBase_USR]]
33+
}
34+
class SubConfFromBase: BaseConfFromBase, P1 { // CHECK: [[@LINE]]:7 | class/Swift | SubConfFromBase | [[SubConfFromBase_USR:.*]] | Def
35+
// CHECK: [[@LINE-1]]:7 | instance-method/Swift | foo() | [[BaseConfFromBase_foo_USR]] | Impl,RelOver,RelCont | rel: 2
36+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
37+
// CHECK-NEXT: RelCont | class/Swift | SubConfFromBase | [[SubConfFromBase_USR]]
38+
}
39+
40+
protocol P2 { // CHECK: [[@LINE]]:10 | protocol/Swift | P2 | [[P2_USR:.*]] | Def |
41+
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P2_foo_USR:.*]] | Def
42+
}
43+
extension P2 { // CHECK: [[@LINE]]:11 | extension/ext-protocol/Swift | P2 | [[P2_ext_USR:.*]] | Def
44+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P2_ext_foo_USR:.*]] | Def,Dyn,RelChild,RelOver | rel: 2
45+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
46+
// CHECK-NEXT: RelChild | extension/ext-protocol/Swift | P2 | [[P2_ext_USR]]
47+
}
48+
49+
struct ConfFromDefaultImpl: P2 { // CHECK: [[@LINE]]:8 | struct/Swift | ConfFromDefaultImpl | [[ConfFromDefaultImpl_USR:.*]] | Def
50+
// CHECK: [[@LINE-1]]:8 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
51+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
52+
// CHECK-NEXT: RelCont | struct/Swift | ConfFromDefaultImpl | [[ConfFromDefaultImpl_USR]]
53+
}
54+
55+
protocol P3 {
56+
func meth1() // CHECK: [[@LINE]]:8 | instance-method/Swift | meth1() | [[P3_meth1_USR:.*]] | Def
57+
func meth2() // CHECK: [[@LINE]]:8 | instance-method/Swift | meth2() | [[P3_meth2_USR:.*]] | Def
58+
}
59+
60+
class BaseMultiConf {
61+
func meth2() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | meth2() | [[BaseMultiConf_meth2_USR:.*]] | Def
62+
}
63+
extension SubMultiConf {
64+
func meth1() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | meth1() | [[SubMultiConf_ext_meth1_USR:.*]] | Def
65+
}
66+
class SubMultiConf: BaseMultiConf,P2,P1,P3 { // CHECK: [[@LINE]]:7 | class/Swift | SubMultiConf | [[SubMultiConf_USR:.*]] | Def
67+
// CHECK: [[@LINE-1]]:7 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
68+
// CHECK-NEXT RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
69+
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
70+
// CHECK: [[@LINE-4]]:7 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
71+
// CHECK-NEXT RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
72+
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
73+
// CHECK: [[@LINE-7]]:7 | instance-method/Swift | meth1() | [[SubMultiConf_ext_meth1_USR]] | Impl,RelOver,RelCont | rel: 2
74+
// CHECK-NEXT RelOver | instance-method/Swift | meth1() | [[P3_meth1_USR]]
75+
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
76+
// CHECK: [[@LINE-10]]:7 | instance-method/Swift | meth2() | [[BaseMultiConf_meth2_USR]] | Impl,RelOver,RelCont | rel: 2
77+
// CHECK-NEXT RelOver | instance-method/Swift | meth2() | [[P3_meth2_USR]]
78+
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
79+
// CHECK-NOT: [[@LINE-13]]:7 | instance-method
80+
}
81+
82+
protocol InheritingP: P1 { // CHECK: [[@LINE]]:10 | protocol/Swift | InheritingP | [[InheritingP_USR:.*]] | Def
83+
// FIXME: Should have override relation with P1.foo()
84+
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[InheritingP_foo_USR:.*]] | Def,Dyn,RelChild | rel: 1
85+
// CHECK-NEXT: RelChild | protocol/Swift | InheritingP | [[InheritingP_USR]]
86+
}
87+
88+
struct DirectConf2: InheritingP { // CHECK: [[@LINE]]:8 | struct/Swift | DirectConf2 | [[DirectConf2_USR:.*]] | Def
89+
// FIXME: Should only override InheritingP.foo()
90+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[DirectConf2_foo_USR:.*]] | Def,RelChild,RelOver | rel: 3
91+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[InheritingP_foo_USR]]
92+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
93+
// CHECK-NEXT: RelChild | struct/Swift | DirectConf2 | [[DirectConf2_USR]]
94+
}
95+
96+
extension InheritingP { // CHECK: [[@LINE]]:11 | extension/ext-protocol/Swift | InheritingP | [[InheritingP_USR:.*]] | Def
97+
// FIXME: Should only override InheritingP.foo()
98+
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[InheritingP_ext_foo_USR:.*]] | Def,Dyn,RelChild,RelOver | rel: 3
99+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[InheritingP_foo_USR]]
100+
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
101+
// CHECK-NEXT: RelChild | extension/ext-protocol/Swift | InheritingP | [[InheritingP_USR]]
102+
}

0 commit comments

Comments
 (0)