| //===--- 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/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/ProtocolConformance.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/AST/TypeRepr.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" |
| #include <tuple> |
| |
| 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->getName(); |
| return false; |
| case AccessorKind::Set: |
| OS << "setter:" << ASD->getName(); |
| return false; |
| case AccessorKind::DidSet: |
| OS << "didSet:" << ASD->getName(); |
| return false; |
| case AccessorKind::WillSet: |
| OS << "willSet:" << ASD->getName() ; |
| return false; |
| |
| 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->getName(); |
| return false; |
| } |
| |
| static bool isMemberwiseInit(swift::ValueDecl *D) { |
| if (auto AFD = dyn_cast<AbstractFunctionDecl>(D)) |
| return AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::MemberwiseInitializer; |
| return false; |
| } |
| |
| static SourceLoc getLocForExtension(ExtensionDecl *D) { |
| // Use the 'End' token of the range, in case it is a compound name, e.g. |
| // extension A.B {} |
| // we want the location of 'B' token. |
| if (auto *repr = D->getExtendedTypeRepr()) { |
| return repr->getSourceRange().End; |
| } |
| return SourceLoc(); |
| } |
| |
| 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<ImportedModule> &Modules) const { |
| constexpr ModuleDecl::ImportFilter ImportFilter = { |
| ModuleDecl::ImportFilterKind::Exported, |
| ModuleDecl::ImportFilterKind::Default, |
| ModuleDecl::ImportFilterKind::ImplementationOnly}; |
| |
| if (auto *SF = SFOrMod.dyn_cast<SourceFile *>()) { |
| SF->getImportedModules(Modules, ImportFilter); |
| } else { |
| SFOrMod.get<ModuleDecl *>()->getImportedModules(Modules, ImportFilter); |
| } |
| } |
| }; |
| |
| struct IndexedWitness { |
| ValueDecl *Member; |
| ValueDecl *Requirement; |
| }; |
| |
| 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<IndexedWitness, 6> ExplicitWitnesses; |
| SmallVector<SourceLoc, 6> RefsToSuppress; |
| }; |
| SmallVector<Entity, 6> EntitiesStack; |
| SmallVector<Expr *, 8> ExprStack; |
| SmallVector<const AccessorDecl *, 4> ManuallyVisitedAccessorStack; |
| bool Cancelled = false; |
| |
| struct NameAndUSR { |
| StringRef USR; |
| StringRef name; |
| }; |
| typedef llvm::PointerIntPair<Decl *, 3> DeclAccessorPair; |
| llvm::DenseMap<void *, 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::printValueDeclUSR(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 getModuleNameAndUSR(ModuleEntity Mod, StringRef &name, StringRef &USR) { |
| auto &result = nameAndUSRCache[Mod.getOpaqueValue()]; |
| if (result.USR.empty()) { |
| SmallString<128> storage; |
| { |
| llvm::raw_svector_ostream OS(storage); |
| if (ide::printModuleUSR(Mod, OS)) |
| return true; |
| result.USR = stringStorage.copyString(OS.str()); |
| } |
| storage.clear(); |
| { |
| llvm::raw_svector_ostream OS(storage); |
| OS << Mod.getFullName(); |
| 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, |
| SourceFile *SF = nullptr) |
| : IdxConsumer(IdxConsumer), SrcMgr(Ctx.SourceMgr), |
| BufferID(SF ? SF->getBufferID().getValueOr(-1) : -1), |
| enableWarnings(IdxConsumer.enableWarnings()) {} |
| |
| ~IndexSwiftASTWalker() override { |
| assert(Cancelled || EntitiesStack.empty()); |
| assert(Cancelled || ManuallyVisitedAccessorStack.empty()); |
| } |
| |
| void visitModule(ModuleDecl &Mod); |
| void visitDeclContext(DeclContext *DC); |
| |
| private: |
| bool visitImports(SourceFileOrModule Mod, |
| llvm::SmallPtrSetImpl<ModuleDecl *> &Visited); |
| |
| bool handleSourceOrModuleFile(SourceFileOrModule SFOrMod); |
| |
| bool walkToDeclPre(Decl *D, CharSourceRange Range) override { |
| // Do not handle unavailable decls from other modules. |
| if (IsModuleFile && AvailableAttr::isUnavailable(D)) |
| return false; |
| |
| if (!handleCustomAttrInitRefs(D)) |
| return false; |
| |
| if (auto *AD = dyn_cast<AccessorDecl>(D)) { |
| if (ManuallyVisitedAccessorStack.empty() || |
| ManuallyVisitedAccessorStack.back() != AD) |
| 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; |
| } |
| |
| /// Report calls to the initializers of property wrapper types on wrapped properties. |
| /// |
| /// These may be either explicit: |
| /// `\@Wrapper(initialialValue: 42) var x: Int` |
| /// or implicit: |
| /// `\@Wrapper var x = 10` |
| bool handleCustomAttrInitRefs(Decl * D) { |
| for (auto *customAttr : D->getAttrs().getAttributes<CustomAttr, true>()) { |
| if (customAttr->isImplicit()) |
| continue; |
| |
| if (auto *semanticInit = dyn_cast_or_null<CallExpr>(customAttr->getSemanticInit())) { |
| if (auto *CD = semanticInit->getCalledValue()) { |
| if (!shouldIndex(CD, /*IsRef*/true)) |
| continue; |
| IndexSymbol Info; |
| const auto reprLoc = customAttr->getTypeRepr()->getLoc(); |
| if (initIndexSymbol(CD, reprLoc, /*IsRef=*/true, Info)) |
| continue; |
| Info.roles |= (unsigned)SymbolRole::Call; |
| if (semanticInit->isImplicit()) |
| Info.roles |= (unsigned)SymbolRole::Implicit; |
| if (!startEntity(CD, Info, /*IsRef=*/true) || !finishCurrentEntity()) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void handleMemberwiseInitRefs(Expr *E) { |
| if (!isa<ApplyExpr>(E)) |
| return; |
| |
| auto *DeclRef = dyn_cast<DeclRefExpr>(cast<ApplyExpr>(E)->getFn()); |
| if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl())) |
| return; |
| |
| // get label locations |
| auto *MemberwiseInit = DeclRef->getDecl(); |
| std::vector<SourceLoc> LabelLocs; |
| ArrayRef<Identifier> Labels; |
| auto NameLoc = DeclRef->getNameLoc(); |
| if (NameLoc.isCompound()) { |
| size_t LabelIndex = 0; |
| SourceLoc ArgLoc; |
| while ((ArgLoc = NameLoc.getArgumentLabelLoc(LabelIndex++)).isValid()) { |
| LabelLocs.push_back(ArgLoc); |
| } |
| Labels = MemberwiseInit->getName().getArgumentNames(); |
| } else if (auto *CallParent = dyn_cast_or_null<CallExpr>(getParentExpr())) { |
| LabelLocs = CallParent->getArgumentLabelLocs(); |
| Labels = CallParent->getArgumentLabels(); |
| } |
| |
| if (LabelLocs.empty()) |
| return; |
| |
| assert(Labels.size() == LabelLocs.size()); |
| |
| // match labels to properties |
| auto *TypeContext = |
| MemberwiseInit->getDeclContext()->getSelfNominalTypeDecl(); |
| if (!TypeContext || !shouldIndex(TypeContext, false)) |
| return; |
| |
| unsigned CurLabel = 0; |
| for (auto Member : TypeContext->getMembers()) { |
| auto Prop = dyn_cast<VarDecl>(Member); |
| if (!Prop) |
| continue; |
| |
| if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) |
| continue; |
| |
| if (CurLabel == LabelLocs.size()) |
| break; |
| |
| if (Labels[CurLabel] != Prop->getName()) |
| continue; |
| |
| IndexSymbol Info; |
| if (initIndexSymbol(Prop, LabelLocs[CurLabel++], /*IsRef=*/true, Info)) |
| continue; |
| if (startEntity(Prop, Info, /*IsRef=*/true)) |
| 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 (Data.isImplicit) |
| Info.roles |= (unsigned)SymbolRole::Implicit; |
| |
| if (CtorTyRef) |
| if (!reportRef(CtorTyRef, Loc, Info, Data.AccKind)) |
| return false; |
| if (!reportRef(D, Loc, Info, Data.AccKind)) |
| return false; |
| |
| // If this is a reference to a property wrapper backing property or |
| // projected value, report a reference to the wrapped property too (i.e. |
| // report an occurrence of `foo` in `_foo` and '$foo'). |
| if (auto *VD = dyn_cast<VarDecl>(D)) { |
| if (auto *Wrapped = VD->getOriginalWrappedProperty()) { |
| assert(Range.getByteLength() > 1 && |
| (Range.str().front() == '_' || Range.str().front() == '$')); |
| auto AfterDollar = Loc.getAdvancedLoc(1); |
| reportRef(Wrapped, AfterDollar, Info, None); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool visitModuleReference(ModuleEntity Mod, CharSourceRange Range) override { |
| SourceLoc Loc = Range.getStart(); |
| |
| if (Loc.isInvalid()) |
| return true; |
| |
| IndexSymbol Info; |
| std::tie(Info.line, Info.column, Info.offset) = getLineColAndOffset(Loc); |
| Info.roles |= (unsigned)SymbolRole::Reference; |
| Info.symInfo = getSymbolInfoForModule(Mod); |
| getModuleNameAndUSR(Mod, Info.name, Info.USR); |
| |
| if (auto Container = getContainingDecl()) |
| addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Container); |
| |
| if (!IdxConsumer.startSourceEntity(Info)) { |
| Cancelled = true; |
| return true; |
| } |
| |
| return finishSourceEntity(Info.symInfo, Info.roles); |
| } |
| |
| bool visitCallAsFunctionReference(ValueDecl *D, CharSourceRange Range, |
| ReferenceMetaData Data) override { |
| // Index implicit callAsFunction reference. |
| return visitDeclReference(D, Range, /*CtorTyRef*/ nullptr, |
| /*ExtTyRef*/ nullptr, Type(), Data); |
| } |
| |
| Decl *getParentDecl() const { |
| if (!EntitiesStack.empty()) |
| return EntitiesStack.back().D; |
| return nullptr; |
| } |
| |
| Decl *getContainingDecl() const { |
| for (const auto &Entity: EntitiesStack) { |
| if (isa<AbstractFunctionDecl>(Entity.D) && |
| (Entity.Roles & (SymbolRoleSet)SymbolRole::Definition)) { |
| return Entity.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()[-std::ptrdiff_t(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 reportImplicitConformance(ValueDecl *witness, ValueDecl *requirement, |
| Decl *container); |
| |
| bool startEntity(Decl *D, IndexSymbol &Info, bool IsRef); |
| 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); |
| return finishSourceEntity(CurrEnt.SymInfo, CurrEnt.Roles); |
| } |
| |
| bool finishSourceEntity(SymbolInfo symInfo, SymbolRoleSet roles) { |
| if (!IdxConsumer.finishSourceEntity(symInfo, 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::tuple<unsigned, unsigned, Optional<unsigned>> |
| getLineColAndOffset(SourceLoc Loc) { |
| if (Loc.isInvalid()) |
| return std::make_tuple(0, 0, None); |
| auto lineAndColumn = SrcMgr.getPresumedLineAndColumnForLoc(Loc, BufferID); |
| unsigned offset = SrcMgr.getLocOffsetInBuffer(Loc, BufferID); |
| return std::make_tuple(lineAndColumn.first, lineAndColumn.second, offset); |
| } |
| |
| bool shouldIndex(ValueDecl *D, bool IsRef) const { |
| if (D->isImplicit() && isa<VarDecl>(D) && IsRef) { |
| // Bypass the implicit VarDecls introduced in CaseStmt bodies by using the |
| // canonical VarDecl for these checks instead. |
| D = cast<VarDecl>(D)->getCanonicalVarDecl(); |
| } |
| |
| if (D->isImplicit() && !isa<ConstructorDecl>(D)) |
| return false; |
| |
| // Do not handle non-public imported decls. |
| if (IsModuleFile && !D->isAccessibleFrom(nullptr)) |
| return false; |
| |
| if (!IdxConsumer.indexLocals() && isLocalSymbol(D)) |
| return isa<ParamDecl>(D) && !IsRef && |
| D->getDeclContext()->getContextKind() != DeclContextKind::AbstractClosureExpr; |
| |
| if (D->isPrivateStdlibDecl()) |
| return false; |
| |
| return true; |
| } |
| |
| // Are there members or conformances in \c D that should be indexed? |
| bool shouldIndexMembers(ExtensionDecl *D) { |
| for (auto Member : D->getMembers()) |
| if (auto VD = dyn_cast<ValueDecl>(Member)) |
| if (shouldIndex(VD, /*IsRef=*/false)) |
| return true; |
| |
| for (auto Inherit : D->getInherited()) |
| if (auto T = Inherit.getType()) |
| if (T->getAnyNominal() && |
| shouldIndex(T->getAnyNominal(), /*IsRef=*/false)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Reports all implicit member value decl conformances that \p D introduces |
| /// as implicit overrides at the source location of \p D, and returns the |
| /// explicit ones so we can check against them later on when visiting them as |
| /// members. |
| /// |
| /// \returns false if AST visitation should stop. |
| bool handleWitnesses(Decl *D, SmallVectorImpl<IndexedWitness> &explicitWitnesses); |
| |
| 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(); |
| auto accessor = dyn_cast<AccessorDecl>(Context); |
| if (accessor) |
| ManuallyVisitedAccessorStack.push_back(accessor); |
| walk(Context); |
| if (accessor) |
| ManuallyVisitedAccessorStack.pop_back(); |
| } |
| |
| void IndexSwiftASTWalker::visitModule(ModuleDecl &Mod) { |
| 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; |
| } |
| } |
| } |
| |
| if (SrcFile != nullptr) { |
| IsModuleFile = false; |
| if (!handleSourceOrModuleFile(*SrcFile)) |
| return; |
| walk(*SrcFile); |
| } else { |
| IsModuleFile = true; |
| isSystemModule = Mod.isSystemModule(); |
| if (!handleSourceOrModuleFile(Mod)) |
| return; |
| walk(Mod); |
| } |
| } |
| |
| bool IndexSwiftASTWalker::handleSourceOrModuleFile(SourceFileOrModule SFOrMod) { |
| // Common reporting for TU/module file. |
| |
| 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<ImportedModule, 8> Imports; |
| TopMod.getImportedModules(Imports); |
| |
| llvm::SmallPtrSet<ModuleDecl *, 8> Reported; |
| for (auto Import : Imports) { |
| ModuleDecl *Mod = Import.importedModule; |
| 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::Synthesized: |
| break; |
| case FileUnitKind::SerializedAST: |
| assert(!IsClangModuleOpt.hasValue() && |
| "cannot handle multi-file modules"); |
| IsClangModuleOpt = false; |
| break; |
| case FileUnitKind::ClangModule: |
| case FileUnitKind::DWARFModule: |
| assert(!IsClangModuleOpt.hasValue() && |
| "cannot handle multi-file modules"); |
| IsClangModuleOpt = true; |
| break; |
| } |
| } |
| if (!IsClangModuleOpt.hasValue()) |
| continue; |
| bool IsClangModule = *IsClangModuleOpt; |
| |
| StringRef ModuleName = Mod->getNameStr(); |
| |
| // If this module is an underscored cross-import overlay, use the name |
| // of the underlying module that declared it instead. |
| if (ModuleDecl *Declaring = Mod->getDeclaringModuleIfCrossImportOverlay()) |
| ModuleName = Declaring->getNameStr(); |
| |
| if (!IdxConsumer.startDependency(ModuleName, Path, IsClangModule, |
| Mod->isSystemModule())) |
| return false; |
| if (!IsClangModule) |
| if (!visitImports(*Mod, Visited)) |
| return false; |
| if (!IdxConsumer.finishDependency(IsClangModule)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IndexSwiftASTWalker::handleWitnesses(Decl *D, SmallVectorImpl<IndexedWitness> &explicitWitnesses) { |
| const auto *const IDC = dyn_cast<IterableDeclContext>(D); |
| if (!IDC) |
| return true; |
| |
| const auto DC = IDC->getAsGenericContext(); |
| for (auto *conf : IDC->getLocalConformances()) { |
| if (conf->isInvalid()) |
| continue; |
| |
| // Ignore self-conformances; they're not interesting to show to users. |
| auto normal = dyn_cast<NormalProtocolConformance>(conf->getRootConformance()); |
| if (!normal) |
| continue; |
| |
| normal->forEachValueWitness([&](ValueDecl *req, Witness witness) { |
| if (Cancelled) |
| return; |
| |
| auto *decl = witness.getDecl(); |
| if (decl == nullptr) |
| return; |
| |
| if (decl->getDeclContext() == DC) { |
| explicitWitnesses.push_back({decl, req}); |
| } else { |
| reportImplicitConformance(decl, req, D); |
| } |
| }); |
| |
| normal->forEachTypeWitness( |
| [&](AssociatedTypeDecl *assoc, Type type, TypeDecl *typeDecl) { |
| if (Cancelled) |
| return true; |
| if (typeDecl == nullptr) |
| return false; |
| |
| if (typeDecl->getDeclContext() == DC) { |
| explicitWitnesses.push_back({typeDecl, assoc}); |
| } else { |
| // Report the implicit conformance. |
| reportImplicitConformance(typeDecl, assoc, D); |
| } |
| return false; |
| }); |
| } |
| |
| if (Cancelled) |
| return false; |
| |
| return true; |
| } |
| |
| bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info, bool IsRef) { |
| switch (IdxConsumer.startSourceEntity(Info)) { |
| case swift::index::IndexDataConsumer::Abort: |
| Cancelled = true; |
| LLVM_FALLTHROUGH; |
| case swift::index::IndexDataConsumer::Skip: |
| return false; |
| case swift::index::IndexDataConsumer::Continue: { |
| SmallVector<IndexedWitness, 6> explicitWitnesses; |
| if (!IsRef) { |
| if (!handleWitnesses(D, explicitWitnesses)) |
| return false; |
| } |
| EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitWitnesses), {}}); |
| return true; |
| } |
| } |
| |
| llvm_unreachable("Unhandled IndexDataConsumer in switch."); |
| } |
| |
| bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) { |
| if (!shouldIndex(D, /*IsRef=*/false)) |
| return false; |
| |
| SourceLoc Loc = D->getLoc(/*SerializedOK*/false); |
| 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: collectAllOverriddenDecls(D, /*IncludeProtocolReqs=*/false)) { |
| addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden); |
| } |
| |
| if (auto Parent = getParentDecl()) { |
| for (const IndexedWitness &witness : EntitiesStack.back().ExplicitWitnesses) { |
| if (witness.Member == D) |
| addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, witness.Requirement); |
| } |
| if (auto ParentVD = dyn_cast<ValueDecl>(Parent)) { |
| SymbolRoleSet RelationsToParent = (SymbolRoleSet)SymbolRole::RelationChildOf; |
| if (Info.symInfo.SubKind == SymbolSubKind::AccessorGetter || |
| 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, /*IsRef=*/false); |
| } |
| |
| 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()->getLoc(); |
| 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->getUnderlyingType()) { |
| 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()->getSelfNominalTypeDecl()) { |
| 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 (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) { |
| SourceLoc Loc = getLocForExtension(D); |
| NominalTypeDecl *NTD = D->getExtendedNominal(); |
| if (!NTD) |
| return true; |
| if (!shouldIndex(NTD, /*IsRef=*/false)) |
| return true; |
| |
| // Don't index "empty" extensions in imported modules. |
| if (IsModuleFile && !shouldIndexMembers(D)) |
| return true; |
| |
| IndexSymbol Info; |
| if (initIndexSymbol(D, NTD, Loc, Info)) |
| return true; |
| |
| if (!startEntity(D, Info, /*IsRef=*/false)) |
| 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)) { |
| bool usedPseudoAccessors = false; |
| if (isa<VarDecl>(D) && |
| !StoreD->getParsedAccessor(AccessorKind::Get) && |
| !StoreD->getParsedAccessor(AccessorKind::Set)) { |
| 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; |
| |
| ManuallyVisitedAccessorStack.push_back(accessor); |
| SourceEntityWalker::walk(cast<Decl>(accessor)); |
| ManuallyVisitedAccessorStack.pop_back(); |
| if (Cancelled) |
| return false; |
| } |
| } else if (auto NTD = dyn_cast<NominalTypeDecl>(D)) { |
| if (!reportInheritedTypeRefs(NTD->getInherited(), NTD)) |
| return false; |
| } |
| } else { |
| // Even if we don't record a local property we still need to walk its |
| // accessor bodies. |
| if (auto StoreD = dyn_cast<AbstractStorageDecl>(D)) { |
| StoreD->visitParsedAccessors([&](AccessorDecl *accessor) { |
| if (Cancelled) |
| return; |
| ManuallyVisitedAccessorStack.push_back(accessor); |
| SourceEntityWalker::walk(cast<Decl>(accessor)); |
| ManuallyVisitedAccessorStack.pop_back(); |
| }); |
| } |
| } |
| |
| 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, /*IsRef=*/true)) |
| 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::reportImplicitConformance(ValueDecl *witness, ValueDecl *requirement, |
| Decl *container) { |
| if (!shouldIndex(witness, /*IsRef=*/true)) |
| return true; // keep walking |
| |
| SourceLoc loc; |
| if (auto *extD = dyn_cast<ExtensionDecl>(container)) |
| loc = getLocForExtension(extD); |
| else |
| loc = container->getLoc(/*SerializedOK*/false); |
| |
| IndexSymbol info; |
| if (initIndexSymbol(witness, loc, /*IsRef=*/true, info)) |
| return true; |
| if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, requirement)) |
| return true; |
| if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationContainedBy, container)) |
| return true; |
| // Remove the 'ref' role that \c initIndexSymbol introduces. This isn't |
| // actually a 'reference', but an 'implicit' override. |
| info.roles &= ~(SymbolRoleSet)SymbolRole::Reference; |
| info.roles |= (SymbolRoleSet)SymbolRole::Implicit; |
| |
| if (!startEntity(witness, info, /*IsRef=*/true)) |
| return true; |
| return finishCurrentEntity(); |
| } |
| |
| bool IndexSwiftASTWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc, |
| bool IsRef, IndexSymbol &Info) { |
| assert(D); |
| if (Loc.isValid() && SrcMgr.findBufferContainingLoc(Loc) != BufferID) |
| return true; |
| if (auto *VD = dyn_cast<VarDecl>(D)) { |
| // Always base the symbol information on the canonical VarDecl |
| D = VD->getCanonicalVarDecl(); |
| } |
| |
| 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; |
| if (auto Container = getContainingDecl()) |
| addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Container); |
| } 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, Info.offset) = getLineColAndOffset(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, Info.offset) = getLineColAndOffset(Loc); |
| if (auto Group = ExtD->getGroupName()) |
| Info.group = Group.getValue(); |
| return false; |
| } |
| |
| static NominalTypeDecl *getNominalParent(ValueDecl *D) { |
| return D->getDeclContext()->getSelfNominalTypeDecl(); |
| } |
| |
| bool IndexSwiftASTWalker::initFuncDeclIndexSymbol(FuncDecl *D, |
| IndexSymbol &Info) { |
| if (initIndexSymbol(D, D->getLoc(/*SerializedOK*/false), /*IsRef=*/false, Info)) |
| return true; |
| |
| if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) { |
| Info.roles |= (SymbolRoleSet)SymbolRole::Dynamic; |
| } |
| |
| if (D->hasImplicitSelfDecl()) { |
| // If this is an @IBAction or @IBSegueAction method, find the sender |
| // parameter (if present) and relate the method to its type. |
| ParamDecl *senderParam = nullptr; |
| |
| auto paramList = D->getParameters(); |
| if (D->getAttrs().hasAttribute<IBActionAttr>()) { |
| if (paramList->size() > 0) |
| senderParam = paramList->get(0); |
| } else if (D->getAttrs().hasAttribute<IBSegueActionAttr>()) { |
| if (paramList->size() > 1) |
| senderParam = paramList->get(1); |
| } |
| |
| if (senderParam) |
| if (auto nominal = senderParam->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 Expr *getUnderlyingFunc(Expr *Fn) { |
| Fn = Fn->getSemanticsProvidingExpr(); |
| if (auto *DRE = dyn_cast<DeclRefExpr>(Fn)) |
| return DRE; |
| if (auto ApplyE = dyn_cast<SelfApplyExpr>(Fn)) |
| return getUnderlyingFunc(ApplyE->getFn()); |
| if (auto *ACE = dyn_cast<AutoClosureExpr>(Fn)) { |
| if (auto *Unwrapped = ACE->getUnwrappedCurryThunkExpr()) |
| return getUnderlyingFunc(Unwrapped); |
| } |
| return Fn; |
| } |
| |
| static bool isBeingCalled(Expr *Target, ArrayRef<Expr*> ExprStack) { |
| if (!Target) |
| return false; |
| Target = getUnderlyingFunc(Target); |
| |
| for (Expr *E: reverse(ExprStack)) { |
| auto *AE = dyn_cast<ApplyExpr>(E); |
| if (!AE || AE->isImplicit()) |
| continue; |
| if (isa<ConstructorRefCallExpr>(AE) && AE->getArg() == Target) |
| return true; |
| if (isa<SelfApplyExpr>(AE)) |
| continue; |
| if (getUnderlyingFunc(AE->getFn()) == Target) |
| return true; |
| } |
| return false; |
| } |
| |
| 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, ExprStack)) |
| 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) |
| return true; |
| for (StringRef tagName : DC->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, Info.offset) = getLineColAndOffset(loc); |
| if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles)) { |
| Cancelled = true; |
| break; |
| } |
| } |
| return !Cancelled; |
| } |
| |
| 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::Synthesized: |
| Info += "synthesized"; |
| 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: |
| case FileUnitKind::DWARFModule: |
| 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 || |
| TopMod.getFiles().front()->getKind() == FileUnitKind::DWARFModule) |
| return; |
| |
| auto It = ImportsMap.find(&TopMod); |
| if (It != ImportsMap.end()) { |
| Visited.insert(It->second.begin(), It->second.end()); |
| return; |
| } |
| |
| ModuleDecl::ImportFilter ImportFilter; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Exported; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Default; |
| // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? |
| SmallVector<ImportedModule, 8> Imports; |
| TopMod.getImportedModules(Imports); |
| |
| for (auto Import : Imports) { |
| collectRecursiveModuleImports(*Import.importedModule, Visited); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Indexing entry points |
| //===----------------------------------------------------------------------===// |
| |
| void index::indexDeclContext(DeclContext *DC, IndexDataConsumer &consumer) { |
| assert(DC); |
| SourceFile *SF = DC->getParentSourceFile(); |
| IndexSwiftASTWalker walker(consumer, DC->getASTContext(), SF); |
| walker.visitDeclContext(DC); |
| consumer.finish(); |
| } |
| |
| void index::indexSourceFile(SourceFile *SF, IndexDataConsumer &consumer) { |
| assert(SF); |
| IndexSwiftASTWalker walker(consumer, SF->getASTContext(), SF); |
| walker.visitModule(*SF->getParentModule()); |
| consumer.finish(); |
| } |
| |
| void index::indexModule(ModuleDecl *module, IndexDataConsumer &consumer) { |
| assert(module); |
| IndexSwiftASTWalker walker(consumer, module->getASTContext()); |
| walker.visitModule(*module); |
| consumer.finish(); |
| } |