[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.
This commit is contained in:
Argyrios Kyrtzidis
2018-08-29 10:31:23 -07:00
committed by GitHub
parent b3a3220e38
commit 3bd80e5e31
2 changed files with 206 additions and 15 deletions

View File

@@ -19,6 +19,7 @@
#include "swift/AST/Expr.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/AST/USRGeneration.h"
#include "swift/Basic/SourceManager.h"
@@ -78,6 +79,13 @@ static bool isMemberwiseInit(swift::ValueDecl *D) {
return false;
}
static SourceLoc getLocForExtension(ExtensionDecl *D) {
// Use the 'End' token of the range, in case it is a compound name, e.g.
// extension A.B {}
// we want the location of 'B' token.
return D->getExtendedTypeLoc().getSourceRange().End;
}
namespace {
// Adapter providing a common interface for a SourceFile/Module.
class SourceFileOrModule {
@@ -121,6 +129,11 @@ public:
}
};
struct ValueWitness {
ValueDecl *Member;
ValueDecl *Requirement;
};
class IndexSwiftASTWalker : public SourceEntityWalker {
IndexDataConsumer &IdxConsumer;
SourceManager &SrcMgr;
@@ -133,6 +146,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
Decl *D;
SymbolInfo SymInfo;
SymbolRoleSet Roles;
SmallVector<ValueWitness, 6> ExplicitValueWitnesses;
SmallVector<SourceLoc, 6> RefsToSuppress;
};
SmallVector<Entity, 6> EntitiesStack;
@@ -319,7 +333,7 @@ private:
IndexSymbol Info;
if (initIndexSymbol(Prop, *LabelIt++, /*IsRef=*/true, Info))
continue;
if (startEntity(Prop, Info))
if (startEntity(Prop, Info, /*IsRef=*/true))
finishCurrentEntity();
}
}
@@ -398,8 +412,10 @@ private:
bool reportExtension(ExtensionDecl *D);
bool reportRef(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info,
Optional<AccessKind> AccKind);
bool reportImplicitValueConformance(ValueDecl *witness, ValueDecl *requirement,
Decl *container);
bool startEntity(Decl *D, IndexSymbol &Info);
bool startEntity(Decl *D, IndexSymbol &Info, bool IsRef);
bool startEntityDecl(ValueDecl *D);
bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related);
@@ -459,6 +475,14 @@ private:
return true;
}
/// Reports all implicit member value decl conformances that \p D introduces
/// as implicit overrides at the source location of \p D, and returns the
/// explicit ones so we can check against them later on when visiting them as
/// members.
///
/// \returns false if AST visitation should stop.
bool handleValueWitnesses(Decl *D, SmallVectorImpl<ValueWitness> &explicitValueWitnesses);
void getModuleHash(SourceFileOrModule SFOrMod, llvm::raw_ostream &OS);
llvm::hash_code hashModule(llvm::hash_code code, SourceFileOrModule SFOrMod);
llvm::hash_code hashFileReference(llvm::hash_code code,
@@ -612,16 +636,53 @@ bool IndexSwiftASTWalker::visitImports(
return true;
}
bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info) {
bool IndexSwiftASTWalker::handleValueWitnesses(Decl *D, SmallVectorImpl<ValueWitness> &explicitValueWitnesses) {
auto DC = dyn_cast<DeclContext>(D);
if (!DC)
return true;
for (auto *conf : DC->getLocalConformances()) {
if (conf->isInvalid())
continue;
auto normal = conf->getRootNormalConformance();
normal->forEachValueWitness(nullptr,
[&](ValueDecl *req, Witness witness) {
if (Cancelled)
return;
auto *decl = witness.getDecl();
if (decl->getDeclContext() == DC) {
explicitValueWitnesses.push_back(ValueWitness{decl, req});
} else {
// Report the implicit conformance.
reportImplicitValueConformance(decl, req, D);
}
});
}
if (Cancelled)
return false;
return true;
}
bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info, bool IsRef) {
switch (IdxConsumer.startSourceEntity(Info)) {
case swift::index::IndexDataConsumer::Abort:
Cancelled = true;
LLVM_FALLTHROUGH;
case swift::index::IndexDataConsumer::Skip:
return false;
case swift::index::IndexDataConsumer::Continue:
EntitiesStack.push_back({D, Info.symInfo, Info.roles, {}});
case swift::index::IndexDataConsumer::Continue: {
SmallVector<ValueWitness, 6> explicitValueWitnesses;
if (!IsRef) {
if (!handleValueWitnesses(D, explicitValueWitnesses))
return false;
}
EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitValueWitnesses), {}});
return true;
}
}
llvm_unreachable("Unhandled IndexDataConsumer in switch.");
@@ -649,12 +710,15 @@ bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) {
return false;
}
for (auto Overriden: getOverriddenDecls(D, /*IncludeProtocolReqs=*/!isSystemModule)) {
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden))
return false;
for (auto Overriden: getOverriddenDecls(D, /*IncludeProtocolReqs=*/false)) {
addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden);
}
if (auto Parent = getParentDecl()) {
for (const ValueWitness &witness : EntitiesStack.back().ExplicitValueWitnesses) {
if (witness.Member == D)
addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, witness.Requirement);
}
if (auto ParentVD = dyn_cast<ValueDecl>(Parent)) {
SymbolRoleSet RelationsToParent = (SymbolRoleSet)SymbolRole::RelationChildOf;
if (Info.symInfo.SubKind == SymbolSubKind::AccessorGetter ||
@@ -673,7 +737,7 @@ bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) {
}
}
return startEntity(D, Info);
return startEntity(D, Info, /*IsRef=*/false);
}
bool IndexSwiftASTWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit,
@@ -836,10 +900,7 @@ IndexSwiftASTWalker::getTypeLocAsNominalTypeDecl(const TypeLoc &Ty) {
}
bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) {
// Use the 'End' token of the range, in case it is a compound name, e.g.
// extension A.B {}
// we want the location of 'B' token.
SourceLoc Loc = D->getExtendedTypeLoc().getSourceRange().End;
SourceLoc Loc = getLocForExtension(D);
NominalTypeDecl *NTD = D->getExtendedNominal();
if (!NTD)
return true;
@@ -850,7 +911,7 @@ bool IndexSwiftASTWalker::reportExtension(ExtensionDecl *D) {
if (initIndexSymbol(D, NTD, Loc, Info))
return true;
if (!startEntity(D, Info))
if (!startEntity(D, Info, /*IsRef=*/false))
return false;
if (!reportRelatedRef(NTD, Loc, /*isImplicit=*/false,
@@ -940,7 +1001,7 @@ bool IndexSwiftASTWalker::reportRef(ValueDecl *D, SourceLoc Loc,
if (isSystemModule && !hasUsefulRoleInSystemModule(Info.roles))
return true;
if (!startEntity(D, Info))
if (!startEntity(D, Info, /*IsRef=*/true))
return true;
// Report the accessors that were utilized.
@@ -961,6 +1022,34 @@ bool IndexSwiftASTWalker::reportRef(ValueDecl *D, SourceLoc Loc,
return finishCurrentEntity();
}
bool IndexSwiftASTWalker::reportImplicitValueConformance(ValueDecl *witness, ValueDecl *requirement,
Decl *container) {
if (!shouldIndex(witness, /*IsRef=*/true))
return true; // keep walking
SourceLoc loc;
if (auto *extD = dyn_cast<ExtensionDecl>(container))
loc = getLocForExtension(extD);
else
loc = container->getLoc();
IndexSymbol info;
if (initIndexSymbol(witness, loc, /*IsRef=*/true, info))
return true;
if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, requirement))
return true;
if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationContainedBy, container))
return true;
// Remove the 'ref' role that \c initIndexSymbol introduces. This isn't
// actually a 'reference', but an 'implicit' override.
info.roles &= ~(SymbolRoleSet)SymbolRole::Reference;
info.roles |= (SymbolRoleSet)SymbolRole::Implicit;
if (!startEntity(witness, info, /*IsRef=*/true))
return true;
return finishCurrentEntity();
}
bool IndexSwiftASTWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc,
bool IsRef, IndexSymbol &Info) {
assert(D);

View File

@@ -0,0 +1,102 @@
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s
protocol P1 { // CHECK: [[@LINE]]:10 | protocol/Swift | P1 | [[P1_USR:.*]] | Def |
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P1_foo_USR:.*]] | Def
}
struct DirectConf: P1 { // CHECK: [[@LINE]]:8 | struct/Swift | DirectConf | [[DirectConf_USR:.*]] | Def
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[DirectConf_foo_USR:.*]] | Def,RelChild,RelOver | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelChild | struct/Swift | DirectConf | [[DirectConf_USR]]
}
struct ConfFromExtension {}
extension ConfFromExtension: P1 { // CHECK: [[@LINE]]:11 | extension/ext-struct/Swift | ConfFromExtension | [[ConfFromExtension_ext_USR:.*]] | Def
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[ConfFromExtension_ext_foo_USR:.*]] | Def,RelChild,RelOver | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelChild | extension/ext-struct/Swift | ConfFromExtension | [[ConfFromExtension_ext_USR]]
}
struct ImplicitConfFromExtension { // CHECK: [[@LINE]]:8 | struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR:.*]] | Def
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[ImplicitConfFromExtension_foo_USR:.*]] | Def,RelChild | rel: 1
// CHECK-NEXT: RelChild | struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR]]
}
extension ImplicitConfFromExtension: P1 { // CHECK: [[@LINE]]:11 | extension/ext-struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR:.*]] | Def
// CHECK: [[@LINE-1]]:11 | instance-method/Swift | foo() | [[ImplicitConfFromExtension_foo_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelCont | extension/ext-struct/Swift | ImplicitConfFromExtension | [[ImplicitConfFromExtension_USR]]
}
class BaseConfFromBase { // CHECK: [[@LINE]]:7 | class/Swift | BaseConfFromBase | [[BaseConfFromBase_USR:.*]] | Def
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[BaseConfFromBase_foo_USR:.*]] | Def,Dyn,RelChild | rel: 1
// CHECK-NEXT: RelChild | class/Swift | BaseConfFromBase | [[BaseConfFromBase_USR]]
}
class SubConfFromBase: BaseConfFromBase, P1 { // CHECK: [[@LINE]]:7 | class/Swift | SubConfFromBase | [[SubConfFromBase_USR:.*]] | Def
// CHECK: [[@LINE-1]]:7 | instance-method/Swift | foo() | [[BaseConfFromBase_foo_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelCont | class/Swift | SubConfFromBase | [[SubConfFromBase_USR]]
}
protocol P2 { // CHECK: [[@LINE]]:10 | protocol/Swift | P2 | [[P2_USR:.*]] | Def |
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P2_foo_USR:.*]] | Def
}
extension P2 { // CHECK: [[@LINE]]:11 | extension/ext-protocol/Swift | P2 | [[P2_ext_USR:.*]] | Def
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[P2_ext_foo_USR:.*]] | Def,Dyn,RelChild,RelOver | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
// CHECK-NEXT: RelChild | extension/ext-protocol/Swift | P2 | [[P2_ext_USR]]
}
struct ConfFromDefaultImpl: P2 { // CHECK: [[@LINE]]:8 | struct/Swift | ConfFromDefaultImpl | [[ConfFromDefaultImpl_USR:.*]] | Def
// CHECK: [[@LINE-1]]:8 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
// CHECK-NEXT: RelCont | struct/Swift | ConfFromDefaultImpl | [[ConfFromDefaultImpl_USR]]
}
protocol P3 {
func meth1() // CHECK: [[@LINE]]:8 | instance-method/Swift | meth1() | [[P3_meth1_USR:.*]] | Def
func meth2() // CHECK: [[@LINE]]:8 | instance-method/Swift | meth2() | [[P3_meth2_USR:.*]] | Def
}
class BaseMultiConf {
func meth2() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | meth2() | [[BaseMultiConf_meth2_USR:.*]] | Def
}
extension SubMultiConf {
func meth1() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | meth1() | [[SubMultiConf_ext_meth1_USR:.*]] | Def
}
class SubMultiConf: BaseMultiConf,P2,P1,P3 { // CHECK: [[@LINE]]:7 | class/Swift | SubMultiConf | [[SubMultiConf_USR:.*]] | Def
// CHECK: [[@LINE-1]]:7 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT RelOver | instance-method/Swift | foo() | [[P2_foo_USR]]
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
// CHECK: [[@LINE-4]]:7 | instance-method/Swift | foo() | [[P2_ext_foo_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
// CHECK: [[@LINE-7]]:7 | instance-method/Swift | meth1() | [[SubMultiConf_ext_meth1_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT RelOver | instance-method/Swift | meth1() | [[P3_meth1_USR]]
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
// CHECK: [[@LINE-10]]:7 | instance-method/Swift | meth2() | [[BaseMultiConf_meth2_USR]] | Impl,RelOver,RelCont | rel: 2
// CHECK-NEXT RelOver | instance-method/Swift | meth2() | [[P3_meth2_USR]]
// CHECK-NEXT RelCont | class/Swift | SubMultiConf | [[SubMultiConf_USR]]
// CHECK-NOT: [[@LINE-13]]:7 | instance-method
}
protocol InheritingP: P1 { // CHECK: [[@LINE]]:10 | protocol/Swift | InheritingP | [[InheritingP_USR:.*]] | Def
// FIXME: Should have override relation with P1.foo()
func foo() // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[InheritingP_foo_USR:.*]] | Def,Dyn,RelChild | rel: 1
// CHECK-NEXT: RelChild | protocol/Swift | InheritingP | [[InheritingP_USR]]
}
struct DirectConf2: InheritingP { // CHECK: [[@LINE]]:8 | struct/Swift | DirectConf2 | [[DirectConf2_USR:.*]] | Def
// FIXME: Should only override InheritingP.foo()
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[DirectConf2_foo_USR:.*]] | Def,RelChild,RelOver | rel: 3
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[InheritingP_foo_USR]]
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelChild | struct/Swift | DirectConf2 | [[DirectConf2_USR]]
}
extension InheritingP { // CHECK: [[@LINE]]:11 | extension/ext-protocol/Swift | InheritingP | [[InheritingP_USR:.*]] | Def
// FIXME: Should only override InheritingP.foo()
func foo() {} // CHECK: [[@LINE]]:8 | instance-method/Swift | foo() | [[InheritingP_ext_foo_USR:.*]] | Def,Dyn,RelChild,RelOver | rel: 3
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[InheritingP_foo_USR]]
// CHECK-NEXT: RelOver | instance-method/Swift | foo() | [[P1_foo_USR]]
// CHECK-NEXT: RelChild | extension/ext-protocol/Swift | InheritingP | [[InheritingP_USR]]
}