| //===--- 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::IsGetter: |
| OS << "getter:" << ASD->getFullName(); |
| return false; |
| case AccessorKind::IsSetter: |
| OS << "setter:" << ASD->getFullName(); |
| return false; |
| case AccessorKind::IsDidSet: |
| OS << "didSet:" << ASD->getFullName(); |
| return false; |
| case AccessorKind::IsWillSet: |
| OS << "willSet:" << ASD->getFullName() ; |
| return false; |
| |
| case AccessorKind::IsMaterializeForSet: |
| case AccessorKind::IsAddressor: |
| case AccessorKind::IsMutableAddressor: |
| 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->getExtendedType()->getAnyNominal(); |
| 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::SmallPtrSet<ModuleDecl *, 16> &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::IsGetter, /*IsRef=*/false, |
| D->getLoc()); |
| } |
| bool reportPseudoSetterDecl(VarDecl *D) { |
| return reportPseudoAccessor(D, AccessorKind::IsSetter, /*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::SmallPtrSet<ModuleDecl *, 16> &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::SmallPtrSet<ModuleDecl *, 16> &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: |
| case FileUnitKind::Derived: |
| 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->getExtendedType()->getAnyNominal()) { |
| 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; |
| if (!D->getExtendedType()) |
| return true; |
| NominalTypeDecl *NTD = D->getExtendedType()->getAnyNominal(); |
| 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(); |
| }; |
| if (isa<VarDecl>(D) && isNullOrImplicit(StoreD->getGetter()) && |
| isNullOrImplicit(StoreD->getSetter())) { |
| 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; |
| } else { |
| if (auto FD = StoreD->getGetter()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| if (Cancelled) |
| return false; |
| if (auto FD = StoreD->getSetter()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| if (Cancelled) |
| return false; |
| } |
| if (StoreD->hasObservers()) { |
| if (auto FD = StoreD->getWillSetFunc()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| if (Cancelled) |
| return false; |
| if (auto FD = StoreD->getDidSetFunc()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| if (Cancelled) |
| return false; |
| } |
| if (StoreD->hasAddressors()) { |
| if (auto FD = StoreD->getAddressor()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| if (Cancelled) |
| return false; |
| if (auto FD = StoreD->getMutableAddressor()) |
| SourceEntityWalker::walk(cast<Decl>(FD)); |
| } |
| } 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::IsGetter, /*IsRef=*/true, |
| Loc)) |
| return false; |
| if (UsesSetter) |
| if (!reportPseudoAccessor(ASD, AccessorKind::IsSetter, /*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->getParameterLists().size() >= 2) { |
| auto paramList = D->getParameterList(1); |
| 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::Derived: |
| Info += "derived"; |
| 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::SmallPtrSet<ModuleDecl *, 16> &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(); |
| } |