mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Introduce ExtensionDecl::getExtendedNominal() to provide the nominal type declaration that the extension declaration extends. Move most of the existing callers of the callers to getExtendedType() over to getExtendedNominal(), because they don’t need the full type information. ExtensionDecl::getExtendedNominal() is itself not very interesting yet, because it depends on getExtendedType().
1450 lines
45 KiB
C++
1450 lines
45 KiB
C++
//===--- Index.cpp --------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Index/Index.h"
|
|
#include "swift/Index/Utils.h"
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Comment.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/USRGeneration.h"
|
|
#include "swift/Basic/SourceManager.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "swift/IDE/SourceEntityWalker.h"
|
|
#include "swift/Markup/Markup.h"
|
|
#include "swift/Sema/IDETypeChecking.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::index;
|
|
|
|
static bool
|
|
printArtificialName(const swift::AbstractStorageDecl *ASD, AccessorKind AK, llvm::raw_ostream &OS) {
|
|
switch (AK) {
|
|
case AccessorKind::Get:
|
|
OS << "getter:" << ASD->getFullName();
|
|
return false;
|
|
case AccessorKind::Set:
|
|
OS << "setter:" << ASD->getFullName();
|
|
return false;
|
|
case AccessorKind::DidSet:
|
|
OS << "didSet:" << ASD->getFullName();
|
|
return false;
|
|
case AccessorKind::WillSet:
|
|
OS << "willSet:" << ASD->getFullName() ;
|
|
return false;
|
|
|
|
case AccessorKind::MaterializeForSet:
|
|
case AccessorKind::Address:
|
|
case AccessorKind::MutableAddress:
|
|
case AccessorKind::Read:
|
|
case AccessorKind::Modify:
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled AccessorKind in switch.");
|
|
}
|
|
|
|
static bool printDisplayName(const swift::ValueDecl *D, llvm::raw_ostream &OS) {
|
|
if (!D->hasName() && !isa<ParamDecl>(D)) {
|
|
auto *FD = dyn_cast<AccessorDecl>(D);
|
|
if (!FD)
|
|
return true;
|
|
return printArtificialName(FD->getStorage(), FD->getAccessorKind(), OS);
|
|
}
|
|
|
|
OS << D->getFullName();
|
|
return false;
|
|
}
|
|
|
|
static bool isMemberwiseInit(swift::ValueDecl *D) {
|
|
if (auto AFD = dyn_cast<AbstractFunctionDecl>(D))
|
|
return AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::MemberwiseInitializer;
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
// Adapter providing a common interface for a SourceFile/Module.
|
|
class SourceFileOrModule {
|
|
llvm::PointerUnion<SourceFile *, ModuleDecl *> SFOrMod;
|
|
|
|
public:
|
|
SourceFileOrModule(SourceFile &SF) : SFOrMod(&SF) {}
|
|
SourceFileOrModule(ModuleDecl &Mod) : SFOrMod(&Mod) {}
|
|
|
|
SourceFile *getAsSourceFile() const {
|
|
return SFOrMod.dyn_cast<SourceFile *>();
|
|
}
|
|
|
|
ModuleDecl *getAsModule() const { return SFOrMod.dyn_cast<ModuleDecl *>(); }
|
|
|
|
ModuleDecl &getModule() const {
|
|
if (auto SF = SFOrMod.dyn_cast<SourceFile *>())
|
|
return *SF->getParentModule();
|
|
return *SFOrMod.get<ModuleDecl *>();
|
|
}
|
|
|
|
ArrayRef<FileUnit *> getFiles() const {
|
|
return SFOrMod.is<SourceFile *>() ? *SFOrMod.getAddrOfPtr1()
|
|
: SFOrMod.get<ModuleDecl *>()->getFiles();
|
|
}
|
|
|
|
StringRef getFilename() const {
|
|
if (auto *SF = SFOrMod.dyn_cast<SourceFile *>())
|
|
return SF->getFilename();
|
|
return SFOrMod.get<ModuleDecl *>()->getModuleFilename();
|
|
}
|
|
|
|
void
|
|
getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &Modules) const {
|
|
if (auto *SF = SFOrMod.dyn_cast<SourceFile *>()) {
|
|
SF->getImportedModules(Modules, ModuleDecl::ImportFilter::All);
|
|
} else {
|
|
SFOrMod.get<ModuleDecl *>()->getImportedModules(Modules,
|
|
ModuleDecl::ImportFilter::All);
|
|
}
|
|
}
|
|
};
|
|
|
|
class IndexSwiftASTWalker : public SourceEntityWalker {
|
|
IndexDataConsumer &IdxConsumer;
|
|
SourceManager &SrcMgr;
|
|
unsigned BufferID;
|
|
bool enableWarnings;
|
|
|
|
bool IsModuleFile = false;
|
|
bool isSystemModule = false;
|
|
struct Entity {
|
|
Decl *D;
|
|
SymbolInfo SymInfo;
|
|
SymbolRoleSet Roles;
|
|
SmallVector<SourceLoc, 6> RefsToSuppress;
|
|
};
|
|
SmallVector<Entity, 6> EntitiesStack;
|
|
SmallVector<Expr *, 8> ExprStack;
|
|
bool Cancelled = false;
|
|
|
|
struct NameAndUSR {
|
|
StringRef USR;
|
|
StringRef name;
|
|
};
|
|
typedef llvm::PointerIntPair<Decl *, 3> DeclAccessorPair;
|
|
llvm::DenseMap<Decl *, NameAndUSR> nameAndUSRCache;
|
|
llvm::DenseMap<DeclAccessorPair, NameAndUSR> accessorNameAndUSRCache;
|
|
StringScratchSpace stringStorage;
|
|
|
|
bool getNameAndUSR(ValueDecl *D, ExtensionDecl *ExtD,
|
|
StringRef &name, StringRef &USR) {
|
|
auto &result = nameAndUSRCache[ExtD ? (Decl*)ExtD : D];
|
|
if (result.USR.empty()) {
|
|
SmallString<128> storage;
|
|
{
|
|
llvm::raw_svector_ostream OS(storage);
|
|
if (ExtD) {
|
|
if (ide::printExtensionUSR(ExtD, OS))
|
|
return true;
|
|
} else {
|
|
if (ide::printDeclUSR(D, OS))
|
|
return true;
|
|
}
|
|
result.USR = stringStorage.copyString(OS.str());
|
|
}
|
|
|
|
storage.clear();
|
|
{
|
|
llvm::raw_svector_ostream OS(storage);
|
|
printDisplayName(D, OS);
|
|
result.name = stringStorage.copyString(OS.str());
|
|
}
|
|
}
|
|
|
|
name = result.name;
|
|
USR = result.USR;
|
|
return false;
|
|
}
|
|
|
|
bool getPseudoAccessorNameAndUSR(AbstractStorageDecl *D, AccessorKind AK, StringRef &Name, StringRef &USR) {
|
|
assert(static_cast<int>(AK) < 0x111 && "AccessorKind too big for pair");
|
|
DeclAccessorPair key(D, static_cast<int>(AK));
|
|
auto &result = accessorNameAndUSRCache[key];
|
|
if (result.USR.empty()) {
|
|
SmallString<128> storage;
|
|
{
|
|
llvm::raw_svector_ostream OS(storage);
|
|
if (ide::printAccessorUSR(D, AK, OS))
|
|
return true;
|
|
result.USR = stringStorage.copyString(OS.str());
|
|
}
|
|
|
|
storage.clear();
|
|
{
|
|
llvm::raw_svector_ostream OS(storage);
|
|
printArtificialName(D, AK, OS);
|
|
result.name = stringStorage.copyString(OS.str());
|
|
}
|
|
}
|
|
|
|
Name = result.name;
|
|
USR = result.USR;
|
|
return false;
|
|
}
|
|
|
|
bool addRelation(IndexSymbol &Info, SymbolRoleSet RelationRoles, Decl *D) {
|
|
assert(D);
|
|
auto Match = std::find_if(Info.Relations.begin(), Info.Relations.end(),
|
|
[D](IndexRelation R) { return R.decl == D; });
|
|
if (Match != Info.Relations.end()) {
|
|
Match->roles |= RelationRoles;
|
|
Info.roles |= RelationRoles;
|
|
return false;
|
|
}
|
|
|
|
StringRef Name, USR;
|
|
SymbolInfo SymInfo = getSymbolInfoForDecl(D);
|
|
|
|
if (SymInfo.Kind == SymbolKind::Unknown)
|
|
return true;
|
|
if (auto *ExtD = dyn_cast<ExtensionDecl>(D)) {
|
|
NominalTypeDecl *NTD = ExtD->getExtendedNominal();
|
|
if (getNameAndUSR(NTD, ExtD, Name, USR))
|
|
return true;
|
|
} else {
|
|
if (getNameAndUSR(cast<ValueDecl>(D), /*ExtD=*/nullptr, Name, USR))
|
|
return true;
|
|
}
|
|
|
|
Info.Relations.push_back(IndexRelation(RelationRoles, D, SymInfo, Name, USR));
|
|
Info.roles |= RelationRoles;
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
IndexSwiftASTWalker(IndexDataConsumer &IdxConsumer, ASTContext &Ctx,
|
|
unsigned BufferID = -1)
|
|
: IdxConsumer(IdxConsumer), SrcMgr(Ctx.SourceMgr), BufferID(BufferID),
|
|
enableWarnings(IdxConsumer.enableWarnings()) {}
|
|
|
|
~IndexSwiftASTWalker() override { assert(Cancelled || EntitiesStack.empty()); }
|
|
|
|
void visitModule(ModuleDecl &Mod, StringRef Hash);
|
|
void visitDeclContext(DeclContext *DC);
|
|
|
|
private:
|
|
bool visitImports(SourceFileOrModule Mod,
|
|
llvm::SmallPtrSetImpl<ModuleDecl *> &Visited);
|
|
|
|
bool handleSourceOrModuleFile(SourceFileOrModule SFOrMod, StringRef KnownHash,
|
|
bool &HashIsKnown);
|
|
|
|
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
|
|
// Do not handle unavailable decls.
|
|
if (AvailableAttr::isUnavailable(D))
|
|
return false;
|
|
if (auto *AD = dyn_cast<AccessorDecl>(D)) {
|
|
auto *Parent = getParentDecl();
|
|
if (Parent && Parent != AD->getStorage())
|
|
return false; // already handled as part of the var decl.
|
|
}
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
if (!report(VD))
|
|
return false;
|
|
}
|
|
if (auto *ED = dyn_cast<ExtensionDecl>(D))
|
|
return reportExtension(ED);
|
|
return true;
|
|
}
|
|
|
|
bool walkToDeclPost(Decl *D) override {
|
|
if (Cancelled)
|
|
return false;
|
|
|
|
if (getParentDecl() == D)
|
|
return finishCurrentEntity();
|
|
|
|
return true;
|
|
}
|
|
|
|
void handleMemberwiseInitRefs(Expr *E) {
|
|
if (!isa<ConstructorRefCallExpr>(E))
|
|
return;
|
|
|
|
auto *DeclRef = dyn_cast<DeclRefExpr>(cast<ConstructorRefCallExpr>(E)->getFn());
|
|
if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl()))
|
|
return;
|
|
|
|
// get label locations
|
|
auto *MemberwiseInit = DeclRef->getDecl();
|
|
std::vector<SourceLoc> LabelLocs;
|
|
auto NameLoc = DeclRef->getNameLoc();
|
|
if (NameLoc.isCompound()) {
|
|
size_t LabelIndex = 0;
|
|
SourceLoc ArgLoc;
|
|
while ((ArgLoc = NameLoc.getArgumentLabelLoc(LabelIndex++)).isValid()) {
|
|
LabelLocs.push_back(ArgLoc);
|
|
}
|
|
} else if (auto *CallParent = dyn_cast_or_null<CallExpr>(getParentExpr())) {
|
|
LabelLocs = CallParent->getArgumentLabelLocs();
|
|
}
|
|
|
|
if (LabelLocs.empty())
|
|
return;
|
|
|
|
// match labels to properties
|
|
auto *TypeContext = MemberwiseInit->getDeclContext()
|
|
->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
if (!TypeContext || !shouldIndex(TypeContext, false))
|
|
return;
|
|
|
|
auto LabelIt = LabelLocs.begin();
|
|
for (auto Prop : TypeContext->getStoredProperties()) {
|
|
if (Prop->getParentInitializer() && Prop->isLet())
|
|
continue;
|
|
|
|
assert(LabelIt != LabelLocs.end());
|
|
IndexSymbol Info;
|
|
if (initIndexSymbol(Prop, *LabelIt++, /*IsRef=*/true, Info))
|
|
continue;
|
|
if (startEntity(Prop, Info))
|
|
finishCurrentEntity();
|
|
}
|
|
}
|
|
|
|
bool walkToExprPre(Expr *E) override {
|
|
if (Cancelled)
|
|
return false;
|
|
ExprStack.push_back(E);
|
|
|
|
handleMemberwiseInitRefs(E);
|
|
return true;
|
|
}
|
|
|
|
bool walkToExprPost(Expr *E) override {
|
|
if (Cancelled)
|
|
return false;
|
|
assert(ExprStack.back() == E);
|
|
ExprStack.pop_back();
|
|
return true;
|
|
}
|
|
|
|
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
|
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
|
|
ReferenceMetaData Data) override {
|
|
SourceLoc Loc = Range.getStart();
|
|
|
|
if (isRepressed(Loc) || Loc.isInvalid())
|
|
return true;
|
|
|
|
IndexSymbol Info;
|
|
if (CtorTyRef)
|
|
if (!reportRef(CtorTyRef, Loc, Info, Data.AccKind))
|
|
return false;
|
|
if (!reportRef(D, Loc, Info, Data.AccKind))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Decl *getParentDecl() const {
|
|
if (!EntitiesStack.empty())
|
|
return EntitiesStack.back().D;
|
|
return nullptr;
|
|
}
|
|
|
|
void repressRefAtLoc(SourceLoc Loc) {
|
|
if (Loc.isInvalid()) return;
|
|
assert(!EntitiesStack.empty());
|
|
EntitiesStack.back().RefsToSuppress.push_back(Loc);
|
|
}
|
|
|
|
bool isRepressed(SourceLoc Loc) const {
|
|
if (EntitiesStack.empty() || Loc.isInvalid())
|
|
return false;
|
|
auto &Suppressed = EntitiesStack.back().RefsToSuppress;
|
|
return std::find(Suppressed.begin(), Suppressed.end(), Loc) != Suppressed.end();
|
|
|
|
}
|
|
|
|
Expr *getContainingExpr(size_t index) const {
|
|
if (ExprStack.size() > index)
|
|
return ExprStack.end()[-(index + 1)];
|
|
return nullptr;
|
|
}
|
|
|
|
Expr *getCurrentExpr() const {
|
|
return ExprStack.empty() ? nullptr : ExprStack.back();
|
|
}
|
|
|
|
Expr *getParentExpr() const {
|
|
return getContainingExpr(1);
|
|
}
|
|
|
|
|
|
bool report(ValueDecl *D);
|
|
bool reportExtension(ExtensionDecl *D);
|
|
bool reportRef(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info,
|
|
Optional<AccessKind> AccKind);
|
|
|
|
bool startEntity(Decl *D, IndexSymbol &Info);
|
|
bool startEntityDecl(ValueDecl *D);
|
|
|
|
bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related);
|
|
bool reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet Relations, Decl *Related);
|
|
bool reportInheritedTypeRefs(ArrayRef<TypeLoc> Inherited, Decl *Inheritee);
|
|
NominalTypeDecl *getTypeLocAsNominalTypeDecl(const TypeLoc &Ty);
|
|
|
|
bool reportPseudoGetterDecl(VarDecl *D) {
|
|
return reportPseudoAccessor(D, AccessorKind::Get, /*IsRef=*/false,
|
|
D->getLoc());
|
|
}
|
|
bool reportPseudoSetterDecl(VarDecl *D) {
|
|
return reportPseudoAccessor(D, AccessorKind::Set, /*IsRef=*/false,
|
|
D->getLoc());
|
|
}
|
|
bool reportPseudoAccessor(AbstractStorageDecl *D, AccessorKind AccKind,
|
|
bool IsRef, SourceLoc Loc);
|
|
|
|
bool finishCurrentEntity() {
|
|
Entity CurrEnt = EntitiesStack.pop_back_val();
|
|
assert(CurrEnt.SymInfo.Kind != SymbolKind::Unknown);
|
|
if (!IdxConsumer.finishSourceEntity(CurrEnt.SymInfo, CurrEnt.Roles)) {
|
|
Cancelled = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool initIndexSymbol(ValueDecl *D, SourceLoc Loc, bool IsRef,
|
|
IndexSymbol &Info);
|
|
bool initIndexSymbol(ExtensionDecl *D, ValueDecl *ExtendedD, SourceLoc Loc,
|
|
IndexSymbol &Info);
|
|
bool initFuncDeclIndexSymbol(FuncDecl *D, IndexSymbol &Info);
|
|
bool initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info);
|
|
bool initVarRefIndexSymbols(Expr *CurrentE, ValueDecl *D, SourceLoc Loc,
|
|
IndexSymbol &Info, Optional<AccessKind> AccKind);
|
|
|
|
bool indexComment(const Decl *D);
|
|
|
|
std::pair<unsigned, unsigned> getLineCol(SourceLoc Loc) {
|
|
if (Loc.isInvalid())
|
|
return std::make_pair(0, 0);
|
|
return SrcMgr.getLineAndColumn(Loc, BufferID);
|
|
}
|
|
|
|
bool shouldIndex(ValueDecl *D, bool IsRef) const {
|
|
if (D->isImplicit() && !isa<ConstructorDecl>(D))
|
|
return false;
|
|
|
|
if (!IdxConsumer.indexLocals() && isLocalSymbol(D))
|
|
return isa<ParamDecl>(D) && !IsRef &&
|
|
D->getDeclContext()->getContextKind() != DeclContextKind::AbstractClosureExpr;
|
|
|
|
if (D->isPrivateStdlibDecl())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
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,
|
|
SourceFileOrModule SFOrMod);
|
|
void getRecursiveModuleImports(ModuleDecl &Mod,
|
|
SmallVectorImpl<ModuleDecl *> &Imports);
|
|
void
|
|
collectRecursiveModuleImports(ModuleDecl &Mod,
|
|
llvm::SmallPtrSetImpl<ModuleDecl *> &Visited);
|
|
|
|
template <typename F>
|
|
void warn(F log) {
|
|
if (!enableWarnings)
|
|
return;
|
|
|
|
SmallString<128> warning;
|
|
llvm::raw_svector_ostream OS(warning);
|
|
log(OS);
|
|
}
|
|
|
|
// This maps a module to all its imports, recursively.
|
|
llvm::DenseMap<ModuleDecl *, llvm::SmallVector<ModuleDecl *, 4>> ImportsMap;
|
|
};
|
|
} // anonymous namespace
|
|
|
|
void IndexSwiftASTWalker::visitDeclContext(DeclContext *Context) {
|
|
IsModuleFile = false;
|
|
isSystemModule = Context->getParentModule()->isSystemModule();
|
|
walk(Context);
|
|
}
|
|
|
|
void IndexSwiftASTWalker::visitModule(ModuleDecl &Mod, StringRef KnownHash) {
|
|
SourceFile *SrcFile = nullptr;
|
|
for (auto File : Mod.getFiles()) {
|
|
if (auto SF = dyn_cast<SourceFile>(File)) {
|
|
auto BufID = SF->getBufferID();
|
|
if (BufID.hasValue() && *BufID == BufferID) {
|
|
SrcFile = SF;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool HashIsKnown;
|
|
if (SrcFile != nullptr) {
|
|
IsModuleFile = false;
|
|
if (!handleSourceOrModuleFile(*SrcFile, KnownHash, HashIsKnown))
|
|
return;
|
|
if (HashIsKnown)
|
|
return; // No need to report symbols.
|
|
walk(*SrcFile);
|
|
} else {
|
|
IsModuleFile = true;
|
|
isSystemModule = Mod.isSystemModule();
|
|
if (!handleSourceOrModuleFile(Mod, KnownHash, HashIsKnown))
|
|
return;
|
|
if (HashIsKnown)
|
|
return; // No need to report symbols.
|
|
walk(Mod);
|
|
}
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::handleSourceOrModuleFile(SourceFileOrModule SFOrMod,
|
|
StringRef KnownHash,
|
|
bool &HashIsKnown) {
|
|
// Common reporting for TU/module file.
|
|
|
|
SmallString<32> HashBuf;
|
|
{
|
|
llvm::raw_svector_ostream HashOS(HashBuf);
|
|
getModuleHash(SFOrMod, HashOS);
|
|
StringRef Hash = HashOS.str();
|
|
HashIsKnown = Hash == KnownHash;
|
|
if (!IdxConsumer.recordHash(Hash, HashIsKnown))
|
|
return false;
|
|
}
|
|
|
|
// We always report the dependencies, even if the hash is known.
|
|
llvm::SmallPtrSet<ModuleDecl *, 16> Visited;
|
|
return visitImports(SFOrMod, Visited);
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::visitImports(
|
|
SourceFileOrModule TopMod, llvm::SmallPtrSetImpl<ModuleDecl *> &Visited) {
|
|
// Dependencies of the stdlib module (like SwiftShims module) are
|
|
// implementation details.
|
|
if (TopMod.getModule().isStdlibModule())
|
|
return true;
|
|
|
|
bool IsNew = Visited.insert(&TopMod.getModule()).second;
|
|
if (!IsNew)
|
|
return true;
|
|
|
|
SmallVector<ModuleDecl::ImportedModule, 8> Imports;
|
|
TopMod.getImportedModules(Imports);
|
|
|
|
llvm::SmallPtrSet<ModuleDecl *, 8> Reported;
|
|
for (auto Import : Imports) {
|
|
ModuleDecl *Mod = Import.second;
|
|
bool NewReport = Reported.insert(Mod).second;
|
|
if (!NewReport)
|
|
continue;
|
|
|
|
// FIXME: Handle modules with multiple source files; these will fail on
|
|
// getModuleFilename() (by returning an empty path). Note that such modules
|
|
// may be heterogeneous.
|
|
StringRef Path = Mod->getModuleFilename();
|
|
if (Path.empty() || Path == TopMod.getFilename())
|
|
continue; // this is a submodule.
|
|
|
|
Optional<bool> IsClangModuleOpt;
|
|
for (auto File : Mod->getFiles()) {
|
|
switch (File->getKind()) {
|
|
case FileUnitKind::Source:
|
|
case FileUnitKind::Builtin:
|
|
break;
|
|
case FileUnitKind::SerializedAST:
|
|
assert(!IsClangModuleOpt.hasValue() &&
|
|
"cannot handle multi-file modules");
|
|
IsClangModuleOpt = false;
|
|
break;
|
|
case FileUnitKind::ClangModule:
|
|
assert(!IsClangModuleOpt.hasValue() &&
|
|
"cannot handle multi-file modules");
|
|
IsClangModuleOpt = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!IsClangModuleOpt.hasValue())
|
|
continue;
|
|
bool IsClangModule = *IsClangModuleOpt;
|
|
|
|
StringRef Hash;
|
|
SmallString<32> HashBuf;
|
|
if (!IsClangModule) {
|
|
llvm::raw_svector_ostream HashOS(HashBuf);
|
|
getModuleHash(*Mod, HashOS);
|
|
Hash = HashOS.str();
|
|
}
|
|
|
|
if (!IdxConsumer.startDependency(Mod->getName().str(), Path, IsClangModule,
|
|
Mod->isSystemModule(), Hash))
|
|
return false;
|
|
if (!IsClangModule)
|
|
if (!visitImports(*Mod, Visited))
|
|
return false;
|
|
if (!IdxConsumer.finishDependency(IsClangModule))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info) {
|
|
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, {}});
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled IndexDataConsumer in switch.");
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) {
|
|
if (!shouldIndex(D, /*IsRef=*/false))
|
|
return false;
|
|
|
|
SourceLoc Loc = D->getLoc();
|
|
if (Loc.isInvalid() && !IsModuleFile)
|
|
return false;
|
|
|
|
if (!IsModuleFile) {
|
|
if (!indexComment(D))
|
|
return false;
|
|
}
|
|
|
|
IndexSymbol Info;
|
|
if (auto FD = dyn_cast<FuncDecl>(D)) {
|
|
if (initFuncDeclIndexSymbol(FD, Info))
|
|
return false;
|
|
} else {
|
|
if (initIndexSymbol(D, Loc, /*IsRef=*/false, Info))
|
|
return false;
|
|
}
|
|
|
|
for (auto Overriden: getOverriddenDecls(D, /*IncludeProtocolReqs=*/!isSystemModule)) {
|
|
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden))
|
|
return false;
|
|
}
|
|
|
|
if (auto Parent = getParentDecl()) {
|
|
if (auto ParentVD = dyn_cast<ValueDecl>(Parent)) {
|
|
SymbolRoleSet RelationsToParent = (SymbolRoleSet)SymbolRole::RelationChildOf;
|
|
if (Info.symInfo.SubKind == SymbolSubKind::AccessorGetter ||
|
|
Info.symInfo.SubKind == SymbolSubKind::AccessorSetter ||
|
|
(Info.symInfo.SubKind >= SymbolSubKind::SwiftAccessorWillSet &&
|
|
Info.symInfo.SubKind <= SymbolSubKind::SwiftAccessorMutableAddressor))
|
|
RelationsToParent |= (SymbolRoleSet)SymbolRole::RelationAccessorOf;
|
|
if (addRelation(Info, RelationsToParent, ParentVD))
|
|
return false;
|
|
|
|
} else if (auto ParentED = dyn_cast<ExtensionDecl>(Parent)) {
|
|
if (ParentED->getExtendedNominal()) {
|
|
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationChildOf, ParentED))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return startEntity(D, Info);
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit,
|
|
SymbolRoleSet Relations, Decl *Related) {
|
|
if (!shouldIndex(D, /*IsRef=*/true))
|
|
return true;
|
|
|
|
IndexSymbol Info;
|
|
if (addRelation(Info, Relations, Related))
|
|
return true;
|
|
if (isImplicit)
|
|
Info.roles |= (unsigned)SymbolRole::Implicit;
|
|
|
|
// don't report this ref again when visitDeclReference reports it
|
|
repressRefAtLoc(Loc);
|
|
|
|
if (!reportRef(D, Loc, Info, None)) {
|
|
Cancelled = true;
|
|
return false;
|
|
}
|
|
|
|
return !Cancelled;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::reportInheritedTypeRefs(ArrayRef<TypeLoc> Inherited, Decl *Inheritee) {
|
|
for (auto Base : Inherited) {
|
|
if (!reportRelatedTypeRef(Base, (SymbolRoleSet) SymbolRole::RelationBaseOf, Inheritee))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet Relations, Decl *Related) {
|
|
|
|
if (auto *T = dyn_cast_or_null<IdentTypeRepr>(Ty.getTypeRepr())) {
|
|
auto Comps = T->getComponentRange();
|
|
SourceLoc IdLoc = Comps.back()->getIdLoc();
|
|
NominalTypeDecl *NTD = nullptr;
|
|
bool isImplicit = false;
|
|
if (auto *VD = Comps.back()->getBoundDecl()) {
|
|
if (auto *TAD = dyn_cast<TypeAliasDecl>(VD)) {
|
|
IndexSymbol Info;
|
|
if (!reportRef(TAD, IdLoc, Info, None))
|
|
return false;
|
|
if (auto Ty = TAD->getUnderlyingTypeLoc().getType()) {
|
|
NTD = Ty->getAnyNominal();
|
|
isImplicit = true;
|
|
}
|
|
} else {
|
|
NTD = dyn_cast<NominalTypeDecl>(VD);
|
|
}
|
|
}
|
|
if (NTD) {
|
|
if (!reportRelatedRef(NTD, IdLoc, isImplicit, Relations, Related))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (Ty.getType()) {
|
|
if (auto nominal = Ty.getType()->getAnyNominal())
|
|
if (!reportRelatedRef(nominal, Ty.getLoc(), /*isImplicit=*/false, Relations, Related))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool isDynamicVarAccessorOrFunc(ValueDecl *D, SymbolInfo symInfo) {
|
|
if (auto NTD = D->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext()) {
|
|
bool isClassOrProtocol = isa<ClassDecl>(NTD) || isa<ProtocolDecl>(NTD);
|
|
bool isInternalAccessor =
|
|
symInfo.SubKind == SymbolSubKind::SwiftAccessorWillSet ||
|
|
symInfo.SubKind == SymbolSubKind::SwiftAccessorDidSet ||
|
|
symInfo.SubKind == SymbolSubKind::SwiftAccessorAddressor ||
|
|
symInfo.SubKind == SymbolSubKind::SwiftAccessorMutableAddressor;
|
|
if (isClassOrProtocol &&
|
|
symInfo.Kind != SymbolKind::StaticMethod &&
|
|
!isInternalAccessor &&
|
|
!D->isFinal()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::reportPseudoAccessor(AbstractStorageDecl *D,
|
|
AccessorKind AccKind, bool IsRef,
|
|
SourceLoc Loc) {
|
|
if (!shouldIndex(D, IsRef))
|
|
return true; // continue walking.
|
|
|
|
auto updateInfo = [this, D, AccKind](IndexSymbol &Info) {
|
|
if (getPseudoAccessorNameAndUSR(D, AccKind, Info.name, Info.USR))
|
|
return true;
|
|
Info.symInfo.Kind = SymbolKind::Function;
|
|
if (D->getDeclContext()->isTypeContext()) {
|
|
if (D->isStatic()) {
|
|
if (isa<VarDecl>(D) &&
|
|
cast<VarDecl>(D)->getCorrectStaticSpelling() == StaticSpellingKind::KeywordClass)
|
|
Info.symInfo.Kind = SymbolKind::ClassMethod;
|
|
else
|
|
Info.symInfo.Kind = SymbolKind::StaticMethod;
|
|
} else {
|
|
Info.symInfo.Kind = SymbolKind::InstanceMethod;
|
|
}
|
|
}
|
|
Info.symInfo.SubKind = getSubKindForAccessor(AccKind);
|
|
Info.roles |= (SymbolRoleSet)SymbolRole::Implicit;
|
|
Info.group = "";
|
|
if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) {
|
|
Info.roles |= (SymbolRoleSet)SymbolRole::Dynamic;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (IsRef) {
|
|
IndexSymbol Info;
|
|
|
|
// initFuncRefIndexSymbol uses the top of the entities stack as the caller,
|
|
// but in this case the top of the stack is the referenced
|
|
// AbstractStorageDecl.
|
|
assert(getParentDecl() == D);
|
|
auto PreviousTop = EntitiesStack.pop_back_val();
|
|
bool initFailed = initFuncRefIndexSymbol(D, Loc, Info);
|
|
EntitiesStack.push_back(PreviousTop);
|
|
|
|
if (initFailed)
|
|
return true; // continue walking.
|
|
if (updateInfo(Info))
|
|
return true;
|
|
|
|
if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles))
|
|
Cancelled = true;
|
|
} else {
|
|
IndexSymbol Info;
|
|
if (initIndexSymbol(D, Loc, IsRef, Info))
|
|
return true; // continue walking.
|
|
if (updateInfo(Info))
|
|
return true;
|
|
if (addRelation(Info, (SymbolRoleSet)SymbolRole::RelationAccessorOf |
|
|
(SymbolRoleSet)SymbolRole::RelationChildOf , D))
|
|
return true;
|
|
|
|
if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles))
|
|
Cancelled = true;
|
|
}
|
|
return !Cancelled;
|
|
}
|
|
|
|
NominalTypeDecl *
|
|
IndexSwiftASTWalker::getTypeLocAsNominalTypeDecl(const TypeLoc &Ty) {
|
|
if (Type T = Ty.getType())
|
|
return T->getAnyNominal();
|
|
if (auto *T = dyn_cast_or_null<IdentTypeRepr>(Ty.getTypeRepr())) {
|
|
auto Comp = T->getComponentRange().back();
|
|
if (auto NTD = dyn_cast_or_null<NominalTypeDecl>(Comp->getBoundDecl()))
|
|
return NTD;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
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;
|
|
NominalTypeDecl *NTD = D->getExtendedNominal();
|
|
if (!NTD)
|
|
return true;
|
|
if (!shouldIndex(NTD, /*IsRef=*/false))
|
|
return true;
|
|
|
|
IndexSymbol Info;
|
|
if (initIndexSymbol(D, NTD, Loc, Info))
|
|
return true;
|
|
|
|
if (!startEntity(D, Info))
|
|
return false;
|
|
|
|
if (!reportRelatedRef(NTD, Loc, /*isImplicit=*/false,
|
|
(SymbolRoleSet)SymbolRole::RelationExtendedBy, D))
|
|
return false;
|
|
if (!reportInheritedTypeRefs(D->getInherited(), D))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::report(ValueDecl *D) {
|
|
if (startEntityDecl(D)) {
|
|
// Pass accessors.
|
|
if (auto StoreD = dyn_cast<AbstractStorageDecl>(D)) {
|
|
auto isNullOrImplicit = [](const Decl *D) -> bool {
|
|
return !D || D->isImplicit();
|
|
};
|
|
|
|
bool usedPseudoAccessors = false;
|
|
if (isa<VarDecl>(D) && isNullOrImplicit(StoreD->getGetter()) &&
|
|
isNullOrImplicit(StoreD->getSetter())) {
|
|
usedPseudoAccessors = true;
|
|
auto VarD = cast<VarDecl>(D);
|
|
// No actual getter or setter, pass 'pseudo' accessors.
|
|
// We create accessor entities so we can implement the functionality
|
|
// of libclang, which reports implicit method property accessor
|
|
// declarations, invocations, and overrides for properties.
|
|
// Note that an ObjC class subclassing from a Swift class, may still
|
|
// be able to override its non-computed-property-accessors via a
|
|
// method.
|
|
if (!reportPseudoGetterDecl(VarD))
|
|
return false;
|
|
if (!reportPseudoSetterDecl(VarD))
|
|
return false;
|
|
}
|
|
|
|
for (auto accessor : StoreD->getAllAccessors()) {
|
|
// Don't include the implicit getter and setter if we added pseudo
|
|
// accessors above.
|
|
if (usedPseudoAccessors &&
|
|
(accessor->getAccessorKind() == AccessorKind::Get ||
|
|
accessor->getAccessorKind() == AccessorKind::Set))
|
|
continue;
|
|
|
|
SourceEntityWalker::walk(cast<Decl>(accessor));
|
|
if (Cancelled)
|
|
return false;
|
|
}
|
|
} else if (auto NTD = dyn_cast<NominalTypeDecl>(D)) {
|
|
if (!reportInheritedTypeRefs(NTD->getInherited(), NTD))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !Cancelled;
|
|
}
|
|
|
|
static bool hasUsefulRoleInSystemModule(SymbolRoleSet roles) {
|
|
return roles & ((SymbolRoleSet)SymbolRole::Definition |
|
|
(SymbolRoleSet)SymbolRole::Declaration |
|
|
(SymbolRoleSet)SymbolRole::RelationChildOf |
|
|
(SymbolRoleSet)SymbolRole::RelationBaseOf |
|
|
(SymbolRoleSet)SymbolRole::RelationOverrideOf |
|
|
(SymbolRoleSet)SymbolRole::RelationExtendedBy |
|
|
(SymbolRoleSet)SymbolRole::RelationAccessorOf |
|
|
(SymbolRoleSet)SymbolRole::RelationIBTypeOf);
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::reportRef(ValueDecl *D, SourceLoc Loc,
|
|
IndexSymbol &Info,
|
|
Optional<AccessKind> AccKind) {
|
|
if (!shouldIndex(D, /*IsRef=*/true))
|
|
return true; // keep walking
|
|
|
|
if (isa<AbstractFunctionDecl>(D)) {
|
|
if (initFuncRefIndexSymbol(D, Loc, Info))
|
|
return true;
|
|
} else if (isa<AbstractStorageDecl>(D)) {
|
|
if (initVarRefIndexSymbols(getCurrentExpr(), D, Loc, Info, AccKind))
|
|
return true;
|
|
} else {
|
|
if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
|
|
return true;
|
|
}
|
|
|
|
if (isSystemModule && !hasUsefulRoleInSystemModule(Info.roles))
|
|
return true;
|
|
|
|
if (!startEntity(D, Info))
|
|
return true;
|
|
|
|
// Report the accessors that were utilized.
|
|
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
|
|
bool UsesGetter = Info.roles & (SymbolRoleSet)SymbolRole::Read;
|
|
bool UsesSetter = Info.roles & (SymbolRoleSet)SymbolRole::Write;
|
|
|
|
if (UsesGetter)
|
|
if (!reportPseudoAccessor(ASD, AccessorKind::Get, /*IsRef=*/true,
|
|
Loc))
|
|
return false;
|
|
if (UsesSetter)
|
|
if (!reportPseudoAccessor(ASD, AccessorKind::Set, /*IsRef=*/true,
|
|
Loc))
|
|
return false;
|
|
}
|
|
|
|
return finishCurrentEntity();
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc,
|
|
bool IsRef, IndexSymbol &Info) {
|
|
assert(D);
|
|
Info.decl = D;
|
|
Info.symInfo = getSymbolInfoForDecl(D);
|
|
if (Info.symInfo.Kind == SymbolKind::Unknown)
|
|
return true;
|
|
|
|
// Cannot be extension, which is not a ValueDecl.
|
|
|
|
if (IsRef) {
|
|
Info.roles |= (unsigned)SymbolRole::Reference;
|
|
auto Parent = getParentDecl();
|
|
if (Parent && isa<AbstractFunctionDecl>(Parent))
|
|
addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Parent);
|
|
} else {
|
|
Info.roles |= (unsigned)SymbolRole::Definition;
|
|
if (D->isImplicit())
|
|
Info.roles |= (unsigned)SymbolRole::Implicit;
|
|
}
|
|
|
|
if (getNameAndUSR(D, /*ExtD=*/nullptr, Info.name, Info.USR))
|
|
return true;
|
|
|
|
std::tie(Info.line, Info.column) = getLineCol(Loc);
|
|
if (!IsRef) {
|
|
if (auto Group = D->getGroupName())
|
|
Info.group = Group.getValue();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::initIndexSymbol(ExtensionDecl *ExtD, ValueDecl *ExtendedD,
|
|
SourceLoc Loc, IndexSymbol &Info) {
|
|
assert(ExtD && ExtendedD);
|
|
Info.decl = ExtendedD;
|
|
Info.symInfo = getSymbolInfoForDecl(ExtD);
|
|
if (Info.symInfo.Kind == SymbolKind::Unknown)
|
|
return true;
|
|
|
|
Info.roles |= (unsigned)SymbolRole::Definition;
|
|
|
|
if (getNameAndUSR(ExtendedD, ExtD, Info.name, Info.USR))
|
|
return true;
|
|
|
|
std::tie(Info.line, Info.column) = getLineCol(Loc);
|
|
if (auto Group = ExtD->getGroupName())
|
|
Info.group = Group.getValue();
|
|
return false;
|
|
}
|
|
|
|
static NominalTypeDecl *getNominalParent(ValueDecl *D) {
|
|
return D->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::initFuncDeclIndexSymbol(FuncDecl *D,
|
|
IndexSymbol &Info) {
|
|
if (initIndexSymbol(D, D->getLoc(), /*IsRef=*/false, Info))
|
|
return true;
|
|
|
|
if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) {
|
|
Info.roles |= (SymbolRoleSet)SymbolRole::Dynamic;
|
|
}
|
|
|
|
if (D->getAttrs().hasAttribute<IBActionAttr>()) {
|
|
// Relate with type of the first parameter using RelationIBTypeOf.
|
|
if (D->hasImplicitSelfDecl()) {
|
|
auto paramList = D->getParameters();
|
|
if (!paramList->getArray().empty()) {
|
|
auto param = paramList->get(0);
|
|
if (auto nominal = param->getType()->getAnyNominal()) {
|
|
addRelation(Info, (SymbolRoleSet) SymbolRole::RelationIBTypeOf, nominal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto Group = D->getGroupName())
|
|
Info.group = Group.getValue();
|
|
return false;
|
|
}
|
|
|
|
static bool isSuperRefExpr(Expr *E) {
|
|
if (!E)
|
|
return false;
|
|
if (isa<SuperRefExpr>(E))
|
|
return true;
|
|
if (auto LoadE = dyn_cast<LoadExpr>(E))
|
|
return isSuperRefExpr(LoadE->getSubExpr());
|
|
return false;
|
|
}
|
|
|
|
static bool isDynamicCall(Expr *BaseE, ValueDecl *D) {
|
|
// The call is 'dynamic' if the method is not of a struct/enum and the
|
|
// receiver is not 'super'. Note that if the receiver is 'super' that
|
|
// does not mean that the call is statically determined (an extension
|
|
// method may have injected itself in the super hierarchy).
|
|
// For our purposes 'dynamic' means that the method call cannot invoke
|
|
// a method in a subclass.
|
|
auto TyD = getNominalParent(D);
|
|
if (!TyD)
|
|
return false;
|
|
if (isa<StructDecl>(TyD) || isa<EnumDecl>(TyD))
|
|
return false;
|
|
if (isSuperRefExpr(BaseE))
|
|
return false;
|
|
if (BaseE->getType()->is<MetatypeType>())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool isBeingCalled(Expr *Target, Expr *Parent, Expr *GrandParent) {
|
|
if (!Target || !Parent || !isa<ApplyExpr>(Parent))
|
|
return false;
|
|
|
|
if (!isa<SelfApplyExpr>(Parent))
|
|
return cast<ApplyExpr>(Parent)->getFn() == Target;
|
|
|
|
return GrandParent && isa<CallExpr>(GrandParent) &&
|
|
cast<CallExpr>(GrandParent)->getFn() == Parent;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc,
|
|
IndexSymbol &Info) {
|
|
|
|
if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
|
|
return true;
|
|
|
|
Expr *CurrentE = getCurrentExpr();
|
|
if (!CurrentE)
|
|
return false;
|
|
|
|
Expr *ParentE = getParentExpr();
|
|
|
|
if (!isa<AbstractStorageDecl>(D) &&
|
|
!isBeingCalled(CurrentE, ParentE, getContainingExpr(2)))
|
|
return false;
|
|
|
|
Info.roles |= (unsigned)SymbolRole::Call;
|
|
if (auto *Caller = dyn_cast_or_null<AbstractFunctionDecl>(getParentDecl())) {
|
|
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationCalledBy, Caller))
|
|
return true;
|
|
}
|
|
|
|
Expr *BaseE = nullptr;
|
|
if (auto DotE = dyn_cast_or_null<DotSyntaxCallExpr>(ParentE))
|
|
BaseE = DotE->getBase();
|
|
else if (auto MembE = dyn_cast<MemberRefExpr>(CurrentE))
|
|
BaseE = MembE->getBase();
|
|
else if (auto SubsE = dyn_cast<SubscriptExpr>(CurrentE))
|
|
BaseE = SubsE->getBase();
|
|
|
|
if (!BaseE || BaseE == CurrentE)
|
|
return false;
|
|
|
|
if (Type ReceiverTy = BaseE->getType()) {
|
|
if (auto LVT = ReceiverTy->getAs<LValueType>())
|
|
ReceiverTy = LVT->getObjectType();
|
|
else if (auto MetaT = ReceiverTy->getAs<MetatypeType>())
|
|
ReceiverTy = MetaT->getInstanceType();
|
|
|
|
if (auto TyD = ReceiverTy->getAnyNominal()) {
|
|
if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationReceivedBy, TyD))
|
|
return true;
|
|
if (isDynamicCall(BaseE, D))
|
|
Info.roles |= (unsigned)SymbolRole::Dynamic;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::initVarRefIndexSymbols(Expr *CurrentE, ValueDecl *D,
|
|
SourceLoc Loc, IndexSymbol &Info,
|
|
Optional<AccessKind> AccKind) {
|
|
|
|
if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
|
|
return true;
|
|
|
|
if (!CurrentE)
|
|
return false;
|
|
|
|
AccessKind Kind = AccKind.hasValue() ? *AccKind : AccessKind::Read;
|
|
switch (Kind) {
|
|
case swift::AccessKind::Read:
|
|
Info.roles |= (unsigned)SymbolRole::Read;
|
|
break;
|
|
case swift::AccessKind::ReadWrite:
|
|
Info.roles |= (unsigned)SymbolRole::Read;
|
|
LLVM_FALLTHROUGH;
|
|
case swift::AccessKind::Write:
|
|
Info.roles |= (unsigned)SymbolRole::Write;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IndexSwiftASTWalker::indexComment(const Decl *D) {
|
|
// FIXME: Workaround for getting tag locations. We should enhance cmark to
|
|
// keep track of node offsets in the original comment text.
|
|
struct TagLoc {
|
|
StringRef Text;
|
|
SourceLoc Loc;
|
|
};
|
|
SmallVector<TagLoc, 3> tagLocs;
|
|
for (const auto &single : D->getRawComment().Comments) {
|
|
size_t idx = single.RawText.find("- Tag:");
|
|
if (idx != StringRef::npos) {
|
|
tagLocs.push_back(TagLoc{single.RawText,
|
|
single.Range.getStart().getAdvancedLoc(idx)});
|
|
}
|
|
}
|
|
if (tagLocs.empty())
|
|
return true;
|
|
|
|
swift::markup::MarkupContext MC;
|
|
auto DC = getSingleDocComment(MC, D);
|
|
if (!DC.hasValue())
|
|
return true;
|
|
for (StringRef tagName : DC.getValue()->getTags()) {
|
|
tagName = tagName.trim();
|
|
if (tagName.empty())
|
|
continue;
|
|
SourceLoc loc;
|
|
for (const auto &tagLoc : tagLocs) {
|
|
if (tagLoc.Text.contains(tagName)) {
|
|
loc = tagLoc.Loc;
|
|
break;
|
|
}
|
|
}
|
|
if (loc.isInvalid())
|
|
continue;
|
|
IndexSymbol Info;
|
|
Info.decl = nullptr;
|
|
Info.symInfo = SymbolInfo{ SymbolKind::CommentTag, SymbolSubKind::None,
|
|
SymbolLanguage::Swift, SymbolPropertySet() };
|
|
Info.roles |= (unsigned)SymbolRole::Definition;
|
|
Info.name = StringRef();
|
|
SmallString<128> storage;
|
|
{
|
|
llvm::raw_svector_ostream OS(storage);
|
|
OS << "t:" << tagName;
|
|
Info.USR = stringStorage.copyString(OS.str());
|
|
}
|
|
std::tie(Info.line, Info.column) = getLineCol(loc);
|
|
if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles)) {
|
|
Cancelled = true;
|
|
break;
|
|
}
|
|
}
|
|
return !Cancelled;
|
|
}
|
|
|
|
llvm::hash_code
|
|
IndexSwiftASTWalker::hashFileReference(llvm::hash_code code,
|
|
SourceFileOrModule SFOrMod) {
|
|
StringRef Filename = SFOrMod.getFilename();
|
|
if (Filename.empty())
|
|
return code;
|
|
|
|
// FIXME: FileManager for swift ?
|
|
|
|
llvm::sys::fs::file_status Status;
|
|
if (std::error_code Ret = llvm::sys::fs::status(Filename, Status)) {
|
|
// Failure to read the file, just use filename to recover.
|
|
warn([&](llvm::raw_ostream &OS) {
|
|
OS << "failed to stat file: " << Filename << " (" << Ret.message() << ')';
|
|
});
|
|
return hash_combine(code, Filename);
|
|
}
|
|
|
|
// Don't use inode because it can easily change when you update the repository
|
|
// even though the file is supposed to be the same (same size/time).
|
|
code = hash_combine(code, Filename);
|
|
auto mtime = Status.getLastModificationTime().time_since_epoch().count();
|
|
return hash_combine(code, Status.getSize(), mtime);
|
|
}
|
|
|
|
llvm::hash_code IndexSwiftASTWalker::hashModule(llvm::hash_code code,
|
|
SourceFileOrModule SFOrMod) {
|
|
code = hashFileReference(code, SFOrMod);
|
|
|
|
SmallVector<ModuleDecl *, 16> Imports;
|
|
getRecursiveModuleImports(SFOrMod.getModule(), Imports);
|
|
for (auto Import : Imports)
|
|
code = hashFileReference(code, *Import);
|
|
|
|
return code;
|
|
}
|
|
|
|
void IndexSwiftASTWalker::getRecursiveModuleImports(
|
|
ModuleDecl &Mod, SmallVectorImpl<ModuleDecl *> &Imports) {
|
|
auto It = ImportsMap.find(&Mod);
|
|
if (It != ImportsMap.end()) {
|
|
Imports.append(It->second.begin(), It->second.end());
|
|
return;
|
|
}
|
|
|
|
llvm::SmallPtrSet<ModuleDecl *, 16> Visited;
|
|
collectRecursiveModuleImports(Mod, Visited);
|
|
Visited.erase(&Mod);
|
|
|
|
warn([&Imports](llvm::raw_ostream &OS) {
|
|
std::for_each(Imports.begin(), Imports.end(), [&OS](ModuleDecl *M) {
|
|
if (M->getModuleFilename().empty()) {
|
|
std::string Info = "swift::ModuleDecl with empty file name!! \nDetails: \n";
|
|
Info += " name: ";
|
|
Info += M->getName().get();
|
|
Info += "\n";
|
|
|
|
auto Files = M->getFiles();
|
|
std::for_each(Files.begin(), Files.end(), [&](FileUnit *FU) {
|
|
Info += " file unit: ";
|
|
|
|
switch (FU->getKind()) {
|
|
case FileUnitKind::Builtin:
|
|
Info += "builtin";
|
|
break;
|
|
case FileUnitKind::Source:
|
|
Info += "source, file=\"";
|
|
Info += cast<SourceFile>(FU)->getFilename();
|
|
Info += "\"";
|
|
break;
|
|
case FileUnitKind::SerializedAST:
|
|
Info += "serialized ast, file=\"";
|
|
Info += cast<LoadedFile>(FU)->getFilename();
|
|
Info += "\"";
|
|
break;
|
|
case FileUnitKind::ClangModule:
|
|
Info += "clang module, file=\"";
|
|
Info += cast<LoadedFile>(FU)->getFilename();
|
|
Info += "\"";
|
|
}
|
|
|
|
Info += "\n";
|
|
});
|
|
|
|
OS << "swift::ModuleDecl with empty file name! " << Info << "\n";
|
|
}
|
|
});
|
|
});
|
|
|
|
Imports.append(Visited.begin(), Visited.end());
|
|
std::sort(Imports.begin(), Imports.end(), [](ModuleDecl *LHS, ModuleDecl *RHS) {
|
|
return LHS->getModuleFilename() < RHS->getModuleFilename();
|
|
});
|
|
|
|
// Cache it.
|
|
ImportsMap[&Mod].append(Imports.begin(), Imports.end());
|
|
}
|
|
|
|
void IndexSwiftASTWalker::collectRecursiveModuleImports(
|
|
ModuleDecl &TopMod, llvm::SmallPtrSetImpl<ModuleDecl *> &Visited) {
|
|
|
|
bool IsNew = Visited.insert(&TopMod).second;
|
|
if (!IsNew)
|
|
return;
|
|
|
|
// Pure Clang modules are tied to their dependencies, no need to look into its
|
|
// imports.
|
|
// FIXME: What happens if the clang module imports a swift module ? So far
|
|
// the assumption is that the path to the swift module will be fixed, so no
|
|
// need to hash the clang module.
|
|
// FIXME: This is a bit of a hack.
|
|
if (TopMod.getFiles().size() == 1)
|
|
if (TopMod.getFiles().front()->getKind() == FileUnitKind::ClangModule)
|
|
return;
|
|
|
|
auto It = ImportsMap.find(&TopMod);
|
|
if (It != ImportsMap.end()) {
|
|
Visited.insert(It->second.begin(), It->second.end());
|
|
return;
|
|
}
|
|
|
|
SmallVector<ModuleDecl::ImportedModule, 8> Imports;
|
|
TopMod.getImportedModules(Imports, ModuleDecl::ImportFilter::All);
|
|
|
|
for (auto Import : Imports) {
|
|
collectRecursiveModuleImports(*Import.second, Visited);
|
|
}
|
|
}
|
|
|
|
void IndexSwiftASTWalker::getModuleHash(SourceFileOrModule Mod,
|
|
llvm::raw_ostream &OS) {
|
|
// FIXME: Use a longer hash string to minimize possibility for conflicts.
|
|
llvm::hash_code code = hashModule(0, Mod);
|
|
OS << llvm::APInt(64, code).toString(36, /*Signed=*/false);
|
|
}
|
|
|
|
static Type getContextFreeInterfaceType(ValueDecl *VD) {
|
|
if (auto AFD = dyn_cast<AbstractFunctionDecl>(VD)) {
|
|
return AFD->getMethodInterfaceType();
|
|
}
|
|
return VD->getInterfaceType();
|
|
}
|
|
|
|
ArrayRef<ValueDecl*> swift::
|
|
canDeclProvideDefaultImplementationFor(ValueDecl* VD,
|
|
llvm::SmallVectorImpl<ValueDecl*> &Scratch) {
|
|
|
|
// Skip decls that don't have valid names.
|
|
if (!VD->getFullName())
|
|
return {};
|
|
|
|
// Check if VD is from a protocol extension.
|
|
auto P = VD->getDeclContext()->getAsProtocolExtensionContext();
|
|
if (!P)
|
|
return {};
|
|
|
|
// Look up all decls in the protocol's inheritance chain for the ones with
|
|
// the same name with VD.
|
|
ResolvedMemberResult LookupResult =
|
|
resolveValueMember(*P->getInnermostDeclContext(),
|
|
P->getDeclaredInterfaceType(), VD->getFullName());
|
|
|
|
auto VDType = getContextFreeInterfaceType(VD);
|
|
for (auto Mem : LookupResult.getMemberDecls(InterestedMemberKind::All)) {
|
|
if (isa<ProtocolDecl>(Mem->getDeclContext())) {
|
|
if (Mem->isProtocolRequirement() &&
|
|
getContextFreeInterfaceType(Mem)->isEqual(VDType)) {
|
|
// We find a protocol requirement VD can provide default
|
|
// implementation for.
|
|
Scratch.push_back(Mem);
|
|
}
|
|
}
|
|
}
|
|
return Scratch;
|
|
}
|
|
|
|
std::vector<ValueDecl*> swift::
|
|
getOverriddenDecls(ValueDecl *VD, bool IncludeProtocolRequirements,
|
|
bool Transitive) {
|
|
std::vector<ValueDecl*> results;
|
|
|
|
if (auto Overridden = VD->getOverriddenDecl()) {
|
|
results.push_back(Overridden);
|
|
while (Transitive && (Overridden = Overridden->getOverriddenDecl()))
|
|
results.push_back(Overridden);
|
|
}
|
|
|
|
// Collect the protocol requirements this decl is a default impl for
|
|
llvm::SmallVector<ValueDecl*, 2> Buffer;
|
|
for (auto Req : canDeclProvideDefaultImplementationFor(VD, Buffer)) {
|
|
results.push_back(Req);
|
|
}
|
|
|
|
if (IncludeProtocolRequirements) {
|
|
for (auto Satisfied : VD->getSatisfiedProtocolRequirements()) {
|
|
results.push_back(Satisfied);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Indexing entry points
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void index::indexDeclContext(DeclContext *DC, IndexDataConsumer &consumer) {
|
|
assert(DC);
|
|
unsigned bufferId = DC->getParentSourceFile()->getBufferID().getValue();
|
|
IndexSwiftASTWalker walker(consumer, DC->getASTContext(), bufferId);
|
|
walker.visitDeclContext(DC);
|
|
consumer.finish();
|
|
}
|
|
|
|
void index::indexSourceFile(SourceFile *SF, StringRef hash,
|
|
IndexDataConsumer &consumer) {
|
|
assert(SF);
|
|
unsigned bufferID = SF->getBufferID().getValue();
|
|
IndexSwiftASTWalker walker(consumer, SF->getASTContext(), bufferID);
|
|
walker.visitModule(*SF->getParentModule(), hash);
|
|
consumer.finish();
|
|
}
|
|
|
|
void index::indexModule(ModuleDecl *module, StringRef hash,
|
|
IndexDataConsumer &consumer) {
|
|
assert(module);
|
|
IndexSwiftASTWalker walker(consumer, module->getASTContext());
|
|
walker.visitModule(*module, hash);
|
|
consumer.finish();
|
|
}
|