//===- CXIndexDataConsumer.cpp - Index data consumer for libclang----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CXTranslationUnit.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" using namespace clang; using namespace clang::index; using namespace cxindex; using namespace cxcursor; namespace { class IndexingDeclVisitor : public ConstDeclVisitor { CXIndexDataConsumer &DataConsumer; SourceLocation DeclLoc; const DeclContext *LexicalDC; public: IndexingDeclVisitor(CXIndexDataConsumer &dataConsumer, SourceLocation Loc, const DeclContext *lexicalDC) : DataConsumer(dataConsumer), DeclLoc(Loc), LexicalDC(lexicalDC) { } bool VisitFunctionDecl(const FunctionDecl *D) { DataConsumer.handleFunction(D); return true; } bool VisitVarDecl(const VarDecl *D) { DataConsumer.handleVar(D); return true; } bool VisitFieldDecl(const FieldDecl *D) { DataConsumer.handleField(D); return true; } bool VisitMSPropertyDecl(const MSPropertyDecl *D) { return true; } bool VisitEnumConstantDecl(const EnumConstantDecl *D) { DataConsumer.handleEnumerator(D); return true; } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { DataConsumer.handleTypedefName(D); return true; } bool VisitTagDecl(const TagDecl *D) { DataConsumer.handleTagDecl(D); return true; } bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { DataConsumer.handleObjCInterface(D); return true; } bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { DataConsumer.handleObjCProtocol(D); return true; } bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { DataConsumer.handleObjCImplementation(D); return true; } bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { DataConsumer.handleObjCCategory(D); return true; } bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { DataConsumer.handleObjCCategoryImpl(D); return true; } bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { if (isa(LexicalDC) && !D->isThisDeclarationADefinition()) DataConsumer.handleSynthesizedObjCMethod(D, DeclLoc, LexicalDC); else DataConsumer.handleObjCMethod(D, DeclLoc); return true; } bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { DataConsumer.handleObjCProperty(D); return true; } bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { DataConsumer.handleSynthesizedObjCProperty(D); return true; } bool VisitNamespaceDecl(const NamespaceDecl *D) { DataConsumer.handleNamespace(D); return true; } bool VisitUsingDecl(const UsingDecl *D) { return true; } bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { return true; } bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { DataConsumer.handleClassTemplate(D); return true; } bool VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { DataConsumer.handleTagDecl(D); return true; } bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { DataConsumer.handleFunctionTemplate(D); return true; } bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { DataConsumer.handleTypeAliasTemplate(D); return true; } bool VisitImportDecl(const ImportDecl *D) { DataConsumer.importedModule(D); return true; } bool VisitConceptDecl(const ConceptDecl *D) { DataConsumer.handleConcept(D); return true; } }; CXSymbolRole getSymbolRole(SymbolRoleSet Role) { // CXSymbolRole mirrors low 9 bits of clang::index::SymbolRole. return CXSymbolRole(static_cast(Role) & ((1 << 9) - 1)); } } bool CXIndexDataConsumer::handleDeclOccurrence( const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, SourceLocation Loc, ASTNodeInfo ASTNode) { Loc = getASTContext().getSourceManager().getFileLoc(Loc); if (Roles & (unsigned)SymbolRole::Reference) { const NamedDecl *ND = dyn_cast(D); if (!ND) return true; if (auto *ObjCID = dyn_cast_or_null(ASTNode.OrigD)) { if (!ObjCID->isThisDeclarationADefinition() && ObjCID->getLocation() == Loc) { // The libclang API treats this as ObjCClassRef declaration. IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCID); return true; } } if (auto *ObjCPD = dyn_cast_or_null(ASTNode.OrigD)) { if (!ObjCPD->isThisDeclarationADefinition() && ObjCPD->getLocation() == Loc) { // The libclang API treats this as ObjCProtocolRef declaration. IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCPD); return true; } } CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; if (Roles & (unsigned)SymbolRole::Implicit) { Kind = CXIdxEntityRef_Implicit; } CXSymbolRole CXRole = getSymbolRole(Roles); CXCursor Cursor; if (ASTNode.OrigE) { Cursor = cxcursor::MakeCXCursor(ASTNode.OrigE, cast(ASTNode.ContainerDC), getCXTU()); } else { if (ASTNode.OrigD) { if (auto *OrigND = dyn_cast(ASTNode.OrigD)) Cursor = getRefCursor(OrigND, Loc); else Cursor = MakeCXCursor(ASTNode.OrigD, CXTU); } else { Cursor = getRefCursor(ND, Loc); } } handleReference(ND, Loc, Cursor, dyn_cast_or_null(ASTNode.Parent), ASTNode.ContainerDC, ASTNode.OrigE, Kind, CXRole); } else { const DeclContext *LexicalDC = ASTNode.ContainerDC; if (!LexicalDC) { for (const auto &SymRel : Relations) { if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) LexicalDC = dyn_cast(SymRel.RelatedSymbol); } } IndexingDeclVisitor(*this, Loc, LexicalDC).Visit(ASTNode.OrigD); } return !shouldAbort(); } bool CXIndexDataConsumer::handleModuleOccurrence(const ImportDecl *ImportD, const Module *Mod, SymbolRoleSet Roles, SourceLocation Loc) { if (Roles & (SymbolRoleSet)SymbolRole::Declaration) IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD); return !shouldAbort(); } void CXIndexDataConsumer::finish() { indexDiagnostics(); } CXIndexDataConsumer::ObjCProtocolListInfo::ObjCProtocolListInfo( const ObjCProtocolList &ProtList, CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); for (ObjCInterfaceDecl::protocol_iterator I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { SourceLocation Loc = *LI; ObjCProtocolDecl *PD = *I; ProtEntities.push_back(EntityInfo()); IdxCtx.getEntityInfo(PD, ProtEntities.back(), SA); CXIdxObjCProtocolRefInfo ProtInfo = { nullptr, MakeCursorObjCProtocolRef(PD, Loc, IdxCtx.CXTU), IdxCtx.getIndexLoc(Loc) }; ProtInfos.push_back(ProtInfo); if (IdxCtx.shouldSuppressRefs()) IdxCtx.markEntityOccurrenceInFile(PD, Loc); } for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) ProtInfos[i].protocol = &ProtEntities[i]; for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) Prots.push_back(&ProtInfos[i]); } IBOutletCollectionInfo::IBOutletCollectionInfo( const IBOutletCollectionInfo &other) : AttrInfo(CXIdxAttr_IBOutletCollection, other.cursor, other.loc, other.A) { IBCollInfo.attrInfo = this; IBCollInfo.classCursor = other.IBCollInfo.classCursor; IBCollInfo.classLoc = other.IBCollInfo.classLoc; if (other.IBCollInfo.objcClass) { ClassInfo = other.ClassInfo; IBCollInfo.objcClass = &ClassInfo; } else IBCollInfo.objcClass = nullptr; } AttrListInfo::AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx) : SA(IdxCtx), ref_cnt(0) { if (!D->hasAttrs()) return; for (const auto *A : D->attrs()) { CXCursor C = MakeCXCursor(A, D, IdxCtx.CXTU); CXIdxLoc Loc = IdxCtx.getIndexLoc(A->getLocation()); switch (C.kind) { default: Attrs.push_back(AttrInfo(CXIdxAttr_Unexposed, C, Loc, A)); break; case CXCursor_IBActionAttr: Attrs.push_back(AttrInfo(CXIdxAttr_IBAction, C, Loc, A)); break; case CXCursor_IBOutletAttr: Attrs.push_back(AttrInfo(CXIdxAttr_IBOutlet, C, Loc, A)); break; case CXCursor_IBOutletCollectionAttr: IBCollAttrs.push_back(IBOutletCollectionInfo(C, Loc, A)); break; } } for (unsigned i = 0, e = IBCollAttrs.size(); i != e; ++i) { IBOutletCollectionInfo &IBInfo = IBCollAttrs[i]; CXAttrs.push_back(&IBInfo); const IBOutletCollectionAttr * IBAttr = cast(IBInfo.A); SourceLocation InterfaceLocStart = IBAttr->getInterfaceLoc()->getTypeLoc().getBeginLoc(); IBInfo.IBCollInfo.attrInfo = &IBInfo; IBInfo.IBCollInfo.classLoc = IdxCtx.getIndexLoc(InterfaceLocStart); IBInfo.IBCollInfo.objcClass = nullptr; IBInfo.IBCollInfo.classCursor = clang_getNullCursor(); QualType Ty = IBAttr->getInterface(); if (const ObjCObjectType *ObjectTy = Ty->getAs()) { if (const ObjCInterfaceDecl *InterD = ObjectTy->getInterface()) { IdxCtx.getEntityInfo(InterD, IBInfo.ClassInfo, SA); IBInfo.IBCollInfo.objcClass = &IBInfo.ClassInfo; IBInfo.IBCollInfo.classCursor = MakeCursorObjCClassRef(InterD, InterfaceLocStart, IdxCtx.CXTU); } } } for (unsigned i = 0, e = Attrs.size(); i != e; ++i) CXAttrs.push_back(&Attrs[i]); } IntrusiveRefCntPtr AttrListInfo::create(const Decl *D, CXIndexDataConsumer &IdxCtx) { ScratchAlloc SA(IdxCtx); AttrListInfo *attrs = SA.allocate(); return new (attrs) AttrListInfo(D, IdxCtx); } CXIndexDataConsumer::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { for (const auto &Base : D->bases()) { BaseEntities.push_back(EntityInfo()); const NamedDecl *BaseD = nullptr; QualType T = Base.getType(); SourceLocation Loc = getBaseLoc(Base); if (const TypedefType *TDT = T->getAs()) { BaseD = TDT->getDecl(); } else if (const TemplateSpecializationType * TST = T->getAs()) { BaseD = TST->getTemplateName().getAsTemplateDecl(); } else if (const RecordType *RT = T->getAs()) { BaseD = RT->getDecl(); } if (BaseD) IdxCtx.getEntityInfo(BaseD, BaseEntities.back(), SA); CXIdxBaseClassInfo BaseInfo = { nullptr, MakeCursorCXXBaseSpecifier(&Base, IdxCtx.CXTU), IdxCtx.getIndexLoc(Loc) }; BaseInfos.push_back(BaseInfo); } for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) { if (BaseEntities[i].name && BaseEntities[i].USR) BaseInfos[i].base = &BaseEntities[i]; } for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) CXBases.push_back(&BaseInfos[i]); } SourceLocation CXIndexDataConsumer::CXXBasesListInfo::getBaseLoc( const CXXBaseSpecifier &Base) const { SourceLocation Loc = Base.getSourceRange().getBegin(); TypeLoc TL; if (Base.getTypeSourceInfo()) TL = Base.getTypeSourceInfo()->getTypeLoc(); if (TL.isNull()) return Loc; if (QualifiedTypeLoc QL = TL.getAs()) TL = QL.getUnqualifiedLoc(); if (ElaboratedTypeLoc EL = TL.getAs()) return EL.getNamedTypeLoc().getBeginLoc(); if (DependentNameTypeLoc DL = TL.getAs()) return DL.getNameLoc(); if (DependentTemplateSpecializationTypeLoc DTL = TL.getAs()) return DTL.getTemplateNameLoc(); return Loc; } const char *ScratchAlloc::toCStr(StringRef Str) { if (Str.empty()) return ""; if (Str.data()[Str.size()] == '\0') return Str.data(); return copyCStr(Str); } const char *ScratchAlloc::copyCStr(StringRef Str) { char *buf = IdxCtx.StrScratch.Allocate(Str.size() + 1); std::uninitialized_copy(Str.begin(), Str.end(), buf); buf[Str.size()] = '\0'; return buf; } void CXIndexDataConsumer::setASTContext(ASTContext &ctx) { Ctx = &ctx; cxtu::getASTUnit(CXTU)->setASTContext(&ctx); } void CXIndexDataConsumer::setPreprocessor(std::shared_ptr PP) { cxtu::getASTUnit(CXTU)->setPreprocessor(std::move(PP)); } bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) { assert(D); if (!D->getParentFunctionOrMethod()) return false; if (const NamedDecl *ND = dyn_cast(D)) { switch (ND->getFormalLinkage()) { case NoLinkage: case InternalLinkage: return true; case VisibleNoLinkage: case ModuleInternalLinkage: case UniqueExternalLinkage: llvm_unreachable("Not a sema linkage"); case ModuleLinkage: case ExternalLinkage: return false; } } return true; } bool CXIndexDataConsumer::shouldAbort() { if (!CB.abortQuery) return false; return CB.abortQuery(ClientData, nullptr); } void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { if (File && CB.enteredMainFile) { CXIdxClientFile idxFile = CB.enteredMainFile(ClientData, static_cast(const_cast(File)), nullptr); FileMap[File] = idxFile; } } void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, StringRef filename, OptionalFileEntryRef File, bool isImport, bool isAngled, bool isModuleImport) { if (!CB.ppIncludedFile) return; const FileEntry *FE = File ? &File->getFileEntry() : nullptr; ScratchAlloc SA(*this); CXIdxIncludedFileInfo Info = { getIndexLoc(hashLoc), SA.toCStr(filename), static_cast( const_cast(FE)), isImport, isAngled, isModuleImport }; CXIdxClientFile idxFile = CB.ppIncludedFile(ClientData, &Info); FileMap[FE] = idxFile; } void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { if (!CB.importedASTFile) return; Module *Mod = ImportD->getImportedModule(); if (!Mod) return; // If the imported module is part of the top-level module that we're // indexing, it doesn't correspond to an imported AST file. // FIXME: This assumes that AST files and top-level modules directly // correspond, which is unlikely to remain true forever. if (Module *SrcMod = ImportD->getImportedOwningModule()) if (SrcMod->getTopLevelModule() == Mod->getTopLevelModule()) return; FileEntry *FE = nullptr; if (auto File = Mod->getASTFile()) FE = const_cast(&File->getFileEntry()); CXIdxImportedASTFileInfo Info = {static_cast(FE), Mod, getIndexLoc(ImportD->getLocation()), ImportD->isImplicit()}; CXIdxClientASTFile astFile = CB.importedASTFile(ClientData, &Info); (void)astFile; } void CXIndexDataConsumer::importedPCH(const FileEntry *File) { if (!CB.importedASTFile) return; CXIdxImportedASTFileInfo Info = { static_cast( const_cast(File)), /*module=*/nullptr, getIndexLoc(SourceLocation()), /*isImplicit=*/false }; CXIdxClientASTFile astFile = CB.importedASTFile(ClientData, &Info); (void)astFile; } void CXIndexDataConsumer::startedTranslationUnit() { CXIdxClientContainer idxCont = nullptr; if (CB.startedTranslationUnit) idxCont = CB.startedTranslationUnit(ClientData, nullptr); addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); } void CXIndexDataConsumer::indexDiagnostics() { if (!hasDiagnosticCallback()) return; CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(getCXTU()); handleDiagnosticSet(DiagSet); } void CXIndexDataConsumer::handleDiagnosticSet(CXDiagnostic CXDiagSet) { if (!CB.diagnostic) return; CB.diagnostic(ClientData, CXDiagSet, nullptr); } bool CXIndexDataConsumer::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, const DeclContext *LexicalDC, const DeclContext *SemaDC) { if (!CB.indexDeclaration || !D) return false; if (D->isImplicit() && shouldIgnoreIfImplicit(D)) return false; ScratchAlloc SA(*this); getEntityInfo(D, DInfo.EntInfo, SA); if ((!shouldIndexFunctionLocalSymbols() && !DInfo.EntInfo.USR) || Loc.isInvalid()) return false; if (!LexicalDC) LexicalDC = D->getLexicalDeclContext(); if (shouldSuppressRefs()) markEntityOccurrenceInFile(D, Loc); DInfo.entityInfo = &DInfo.EntInfo; DInfo.cursor = Cursor; DInfo.loc = getIndexLoc(Loc); DInfo.isImplicit = D->isImplicit(); DInfo.attributes = DInfo.EntInfo.attributes; DInfo.numAttributes = DInfo.EntInfo.numAttributes; if (!SemaDC) SemaDC = D->getDeclContext(); getContainerInfo(SemaDC, DInfo.SemanticContainer); DInfo.semanticContainer = &DInfo.SemanticContainer; if (LexicalDC == SemaDC) { DInfo.lexicalContainer = &DInfo.SemanticContainer; } else if (isTemplateImplicitInstantiation(D)) { // Implicit instantiations have the lexical context of where they were // instantiated first. We choose instead the semantic context because: // 1) at the time that we see the instantiation we have not seen the // function where it occurred yet. // 2) the lexical context of the first instantiation is not useful // information anyway. DInfo.lexicalContainer = &DInfo.SemanticContainer; } else { getContainerInfo(LexicalDC, DInfo.LexicalContainer); DInfo.lexicalContainer = &DInfo.LexicalContainer; } if (DInfo.isContainer) { getContainerInfo(getEntityContainer(D), DInfo.DeclAsContainer); DInfo.declAsContainer = &DInfo.DeclAsContainer; } CB.indexDeclaration(ClientData, &DInfo); return true; } bool CXIndexDataConsumer::handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, ObjCContainerDeclInfo &ContDInfo) { ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; return handleDecl(D, Loc, Cursor, ContDInfo); } bool CXIndexDataConsumer::handleFunction(const FunctionDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; if (D->hasSkippedBody()) { isSkipped = true; isDef = true; isContainer = false; } DeclInfo DInfo(!D->isFirstDecl(), isDef, isContainer); if (isSkipped) DInfo.flags |= CXIdxDeclFlag_Skipped; return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleVar(const VarDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleField(const FieldDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleEnumerator(const EnumConstantDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleTagDecl(const TagDecl *D) { if (const CXXRecordDecl *CXXRD = dyn_cast(D)) return handleCXXRecordDecl(CXXRD, D); DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleTypedefName(const TypedefNameDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleObjCInterface(const ObjCInterfaceDecl *D) { // For @class forward declarations, suppress them the same way as references. if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) return false; // already occurred. // FIXME: This seems like the wrong definition for redeclaration. bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, isRedeclaration, /*isImplementation=*/false); return handleObjCContainer(D, D->getLocation(), MakeCursorObjCClassRef(D, D->getLocation(), CXTU), ContDInfo); } ScratchAlloc SA(*this); CXIdxBaseClassInfo BaseClass; EntityInfo BaseEntity; BaseClass.cursor = clang_getNullCursor(); if (ObjCInterfaceDecl *SuperD = D->getSuperClass()) { getEntityInfo(SuperD, BaseEntity, SA); SourceLocation SuperLoc = D->getSuperClassLoc(); BaseClass.base = &BaseEntity; BaseClass.cursor = MakeCursorObjCSuperClassRef(SuperD, SuperLoc, CXTU); BaseClass.loc = getIndexLoc(SuperLoc); if (shouldSuppressRefs()) markEntityOccurrenceInFile(SuperD, SuperLoc); } ObjCProtocolList EmptyProtoList; ObjCProtocolListInfo ProtInfo(D->isThisDeclarationADefinition() ? D->getReferencedProtocols() : EmptyProtoList, *this, SA); ObjCInterfaceDeclInfo InterInfo(D); InterInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); InterInfo.ObjCInterDeclInfo.containerInfo = &InterInfo.ObjCContDeclInfo; InterInfo.ObjCInterDeclInfo.superInfo = D->getSuperClass() ? &BaseClass : nullptr; InterInfo.ObjCInterDeclInfo.protocols = &InterInfo.ObjCProtoListInfo; return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); } bool CXIndexDataConsumer::handleObjCImplementation( const ObjCImplementationDecl *D) { ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, /*isRedeclaration=*/true, /*isImplementation=*/true); return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); } bool CXIndexDataConsumer::handleObjCProtocol(const ObjCProtocolDecl *D) { if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) return false; // already occurred. // FIXME: This seems like the wrong definition for redeclaration. bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, isRedeclaration, /*isImplementation=*/false); return handleObjCContainer(D, D->getLocation(), MakeCursorObjCProtocolRef(D, D->getLocation(), CXTU), ContDInfo); } ScratchAlloc SA(*this); ObjCProtocolList EmptyProtoList; ObjCProtocolListInfo ProtListInfo(D->isThisDeclarationADefinition() ? D->getReferencedProtocols() : EmptyProtoList, *this, SA); ObjCProtocolDeclInfo ProtInfo(D); ProtInfo.ObjCProtoRefListInfo = ProtListInfo.getListInfo(); return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); } bool CXIndexDataConsumer::handleObjCCategory(const ObjCCategoryDecl *D) { ScratchAlloc SA(*this); ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); EntityInfo ClassEntity; const ObjCInterfaceDecl *IFaceD = D->getClassInterface(); SourceLocation ClassLoc = D->getLocation(); SourceLocation CategoryLoc = D->IsClassExtension() ? ClassLoc : D->getCategoryNameLoc(); getEntityInfo(IFaceD, ClassEntity, SA); if (shouldSuppressRefs()) markEntityOccurrenceInFile(IFaceD, ClassLoc); ObjCProtocolListInfo ProtInfo(D->getReferencedProtocols(), *this, SA); CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; if (IFaceD) { CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; CatDInfo.ObjCCatDeclInfo.classCursor = MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); } else { CatDInfo.ObjCCatDeclInfo.objcClass = nullptr; CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); } CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); CatDInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); CatDInfo.ObjCCatDeclInfo.protocols = &CatDInfo.ObjCProtoListInfo; return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } bool CXIndexDataConsumer::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { ScratchAlloc SA(*this); const ObjCCategoryDecl *CatD = D->getCategoryDecl(); ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/true); EntityInfo ClassEntity; const ObjCInterfaceDecl *IFaceD = CatD->getClassInterface(); SourceLocation ClassLoc = D->getLocation(); SourceLocation CategoryLoc = D->getCategoryNameLoc(); getEntityInfo(IFaceD, ClassEntity, SA); if (shouldSuppressRefs()) markEntityOccurrenceInFile(IFaceD, ClassLoc); CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; if (IFaceD) { CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; CatDInfo.ObjCCatDeclInfo.classCursor = MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); } else { CatDInfo.ObjCCatDeclInfo.objcClass = nullptr; CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); } CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); CatDInfo.ObjCCatDeclInfo.protocols = nullptr; return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } bool CXIndexDataConsumer::handleObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; if (D->hasSkippedBody()) { isSkipped = true; isDef = true; isContainer = false; } DeclInfo DInfo(!D->isCanonicalDecl(), isDef, isContainer); if (isSkipped) DInfo.flags |= CXIdxDeclFlag_Skipped; return handleDecl(D, Loc, getCursor(D), DInfo); } bool CXIndexDataConsumer::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); auto *DC = D->getDeclContext(); return handleReference(PD, D->getLocation(), getCursor(D), dyn_cast(DC), DC); } bool CXIndexDataConsumer::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, D->getDeclContext()); } bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { ScratchAlloc SA(*this); ObjCPropertyDeclInfo DInfo; EntityInfo GetterEntity; EntityInfo SetterEntity; DInfo.ObjCPropDeclInfo.declInfo = &DInfo; if (ObjCMethodDecl *Getter = D->getGetterMethodDecl()) { getEntityInfo(Getter, GetterEntity, SA); DInfo.ObjCPropDeclInfo.getter = &GetterEntity; } else { DInfo.ObjCPropDeclInfo.getter = nullptr; } if (ObjCMethodDecl *Setter = D->getSetterMethodDecl()) { getEntityInfo(Setter, SetterEntity, SA); DInfo.ObjCPropDeclInfo.setter = &SetterEntity; } else { DInfo.ObjCPropDeclInfo.setter = nullptr; } return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleNamespace(const NamespaceDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), /*isDefinition=*/true, /*isContainer=*/true); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleClassTemplate(const ClassTemplateDecl *D) { return handleCXXRecordDecl(D->getTemplatedDecl(), D); } bool CXIndexDataConsumer::handleFunctionTemplate(const FunctionTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/D->isThisDeclarationADefinition(), /*isContainer=*/D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleConcept(const ConceptDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, const Expr *E, CXIdxEntityRefKind Kind, CXSymbolRole Role) { if (!CB.indexEntityReference) return false; if (!D || !DC) return false; if (Loc.isInvalid()) return false; if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) return false; if (isNotFromSourceFile(D->getLocation())) return false; if (D->isImplicit() && shouldIgnoreIfImplicit(D)) return false; if (shouldSuppressRefs()) { if (markEntityOccurrenceInFile(D, Loc)) return false; // already occurred. } ScratchAlloc SA(*this); EntityInfo RefEntity, ParentEntity; getEntityInfo(D, RefEntity, SA); if (!RefEntity.USR) return false; getEntityInfo(Parent, ParentEntity, SA); ContainerInfo Container; getContainerInfo(DC, Container); CXIdxEntityRefInfo Info = { Kind, Cursor, getIndexLoc(Loc), &RefEntity, Parent ? &ParentEntity : nullptr, &Container, Role }; CB.indexEntityReference(ClientData, &Info); return true; } bool CXIndexDataConsumer::isNotFromSourceFile(SourceLocation Loc) const { if (Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); SourceLocation FileLoc = SM.getFileLoc(Loc); FileID FID = SM.getFileID(FileLoc); return SM.getFileEntryForID(FID) == nullptr; } void CXIndexDataConsumer::addContainerInMap(const DeclContext *DC, CXIdxClientContainer container) { if (!DC) return; ContainerMapTy::iterator I = ContainerMap.find(DC); if (I == ContainerMap.end()) { if (container) ContainerMap[DC] = container; return; } // Allow changing the container of a previously seen DeclContext so we // can handle invalid user code, like a function re-definition. if (container) I->second = container; else ContainerMap.erase(I); } CXIdxClientEntity CXIndexDataConsumer::getClientEntity(const Decl *D) const { if (!D) return nullptr; EntityMapTy::const_iterator I = EntityMap.find(D); if (I == EntityMap.end()) return nullptr; return I->second; } void CXIndexDataConsumer::setClientEntity(const Decl *D, CXIdxClientEntity client) { if (!D) return; EntityMap[D] = client; } bool CXIndexDataConsumer::handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD) { if (RD->isThisDeclarationADefinition()) { ScratchAlloc SA(*this); CXXClassDeclInfo CXXDInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), /*isDefinition=*/RD->isThisDeclarationADefinition()); CXXBasesListInfo BaseList(RD, *this, SA); CXXDInfo.CXXClassInfo.declInfo = &CXXDInfo; CXXDInfo.CXXClassInfo.bases = BaseList.getBases(); CXXDInfo.CXXClassInfo.numBases = BaseList.getNumBases(); if (shouldSuppressRefs()) { // Go through bases and mark them as referenced. for (unsigned i = 0, e = BaseList.getNumBases(); i != e; ++i) { const CXIdxBaseClassInfo *baseInfo = BaseList.getBases()[i]; if (baseInfo->base) { const NamedDecl *BaseD = BaseList.BaseEntities[i].Dcl; SourceLocation Loc = SourceLocation::getFromRawEncoding(baseInfo->loc.int_data); markEntityOccurrenceInFile(BaseD, Loc); } } } return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), CXXDInfo); } DeclInfo DInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), /*isDefinition=*/RD->isThisDeclarationADefinition(), /*isContainer=*/RD->isThisDeclarationADefinition()); return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); } bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc) { if (!D || Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); D = getEntityDecl(D); std::pair LocInfo = SM.getDecomposedLoc(SM.getFileLoc(Loc)); FileID FID = LocInfo.first; if (FID.isInvalid()) return true; const FileEntry *FE = SM.getFileEntryForID(FID); if (!FE) return true; RefFileOccurrence RefOccur(FE, D); std::pair::iterator, bool> res = RefFileOccurrences.insert(RefOccur); return !res.second; // already in map } const NamedDecl *CXIndexDataConsumer::getEntityDecl(const NamedDecl *D) const { assert(D); D = cast(D->getCanonicalDecl()); if (const ObjCImplementationDecl * ImplD = dyn_cast(D)) { return getEntityDecl(ImplD->getClassInterface()); } else if (const ObjCCategoryImplDecl * CatImplD = dyn_cast(D)) { return getEntityDecl(CatImplD->getCategoryDecl()); } else if (const FunctionDecl *FD = dyn_cast(D)) { if (FunctionTemplateDecl *TemplD = FD->getDescribedFunctionTemplate()) return getEntityDecl(TemplD); } else if (const CXXRecordDecl *RD = dyn_cast(D)) { if (ClassTemplateDecl *TemplD = RD->getDescribedClassTemplate()) return getEntityDecl(TemplD); } return D; } const DeclContext * CXIndexDataConsumer::getEntityContainer(const Decl *D) const { const DeclContext *DC = dyn_cast(D); if (DC) return DC; if (const ClassTemplateDecl *ClassTempl = dyn_cast(D)) { DC = ClassTempl->getTemplatedDecl(); } else if (const FunctionTemplateDecl * FuncTempl = dyn_cast(D)) { DC = FuncTempl->getTemplatedDecl(); } return DC; } CXIdxClientContainer CXIndexDataConsumer::getClientContainerForDC(const DeclContext *DC) const { if (!DC) return nullptr; ContainerMapTy::const_iterator I = ContainerMap.find(DC); if (I == ContainerMap.end()) return nullptr; return I->second; } CXIdxClientFile CXIndexDataConsumer::getIndexFile(const FileEntry *File) { if (!File) return nullptr; FileMapTy::iterator FI = FileMap.find(File); if (FI != FileMap.end()) return FI->second; return nullptr; } CXIdxLoc CXIndexDataConsumer::getIndexLoc(SourceLocation Loc) const { CXIdxLoc idxLoc = { {nullptr, nullptr}, 0 }; if (Loc.isInvalid()) return idxLoc; idxLoc.ptr_data[0] = const_cast(this); idxLoc.int_data = Loc.getRawEncoding(); return idxLoc; } void CXIndexDataConsumer::translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { if (Loc.isInvalid()) return; SourceManager &SM = Ctx->getSourceManager(); Loc = SM.getFileLoc(Loc); std::pair LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; if (FID.isInvalid()) return; const FileEntry *FE = SM.getFileEntryForID(FID); if (indexFile) *indexFile = getIndexFile(FE); if (file) *file = const_cast(FE); if (line) *line = SM.getLineNumber(FID, FileOffset); if (column) *column = SM.getColumnNumber(FID, FileOffset); if (offset) *offset = FileOffset; } static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); static CXIdxEntityCXXTemplateKind getEntityKindFromSymbolProperties(SymbolPropertySet K); static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo &EntityInfo, ScratchAlloc &SA) { if (!D) return; D = getEntityDecl(D); EntityInfo.cursor = getCursor(D); EntityInfo.Dcl = D; EntityInfo.IndexCtx = this; SymbolInfo SymInfo = getSymbolInfo(D); EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); EntityInfo.templateKind = getEntityKindFromSymbolProperties(SymInfo.Properties); EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { EntityInfo.AttrList = AttrListInfo::create(D, *this); EntityInfo.attributes = EntityInfo.AttrList->getAttrs(); EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); } if (EntityInfo.kind == CXIdxEntity_Unexposed) return; if (IdentifierInfo *II = D->getIdentifier()) { EntityInfo.name = SA.toCStr(II->getName()); } else if (isa(D) || isa(D) || isa(D)) { EntityInfo.name = nullptr; // anonymous tag/field/namespace. } else { SmallString<256> StrBuf; { llvm::raw_svector_ostream OS(StrBuf); D->printName(OS); } EntityInfo.name = SA.copyCStr(StrBuf.str()); } { SmallString<512> StrBuf; bool Ignore = getDeclCursorUSR(D, StrBuf); if (Ignore) { EntityInfo.USR = nullptr; } else { EntityInfo.USR = SA.copyCStr(StrBuf.str()); } } } void CXIndexDataConsumer::getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo) { ContInfo.cursor = getCursor(cast(DC)); ContInfo.DC = DC; ContInfo.IndexCtx = this; } CXCursor CXIndexDataConsumer::getRefCursor(const NamedDecl *D, SourceLocation Loc) { if (const TypeDecl *TD = dyn_cast(D)) return MakeCursorTypeRef(TD, Loc, CXTU); if (const ObjCInterfaceDecl *ID = dyn_cast(D)) return MakeCursorObjCClassRef(ID, Loc, CXTU); if (const ObjCProtocolDecl *PD = dyn_cast(D)) return MakeCursorObjCProtocolRef(PD, Loc, CXTU); if (const TemplateDecl *Template = dyn_cast(D)) return MakeCursorTemplateRef(Template, Loc, CXTU); if (const NamespaceDecl *Namespace = dyn_cast(D)) return MakeCursorNamespaceRef(Namespace, Loc, CXTU); if (const NamespaceAliasDecl *Namespace = dyn_cast(D)) return MakeCursorNamespaceRef(Namespace, Loc, CXTU); if (const FieldDecl *Field = dyn_cast(D)) return MakeCursorMemberRef(Field, Loc, CXTU); if (const VarDecl *Var = dyn_cast(D)) return MakeCursorVariableRef(Var, Loc, CXTU); return clang_getNullCursor(); } bool CXIndexDataConsumer::shouldIgnoreIfImplicit(const Decl *D) { if (isa(D)) return false; if (isa(D)) return false; if (isa(D)) return false; if (isa(D)) return false; if (isa(D)) return false; return true; } bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { if (const ClassTemplateSpecializationDecl * SD = dyn_cast(D)) { return SD->getSpecializationKind() == TSK_ImplicitInstantiation; } if (const FunctionDecl *FD = dyn_cast(D)) { return FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation; } return false; } static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage Lang) { switch (K) { case SymbolKind::Unknown: case SymbolKind::Module: case SymbolKind::Macro: case SymbolKind::ClassProperty: case SymbolKind::Using: case SymbolKind::TemplateTypeParm: case SymbolKind::TemplateTemplateParm: case SymbolKind::NonTypeTemplateParm: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; case SymbolKind::Struct: return CXIdxEntity_Struct; case SymbolKind::Union: return CXIdxEntity_Union; case SymbolKind::TypeAlias: if (Lang == SymbolLanguage::CXX) return CXIdxEntity_CXXTypeAlias; return CXIdxEntity_Typedef; case SymbolKind::Function: return CXIdxEntity_Function; case SymbolKind::Variable: return CXIdxEntity_Variable; case SymbolKind::Field: if (Lang == SymbolLanguage::ObjC) return CXIdxEntity_ObjCIvar; return CXIdxEntity_Field; case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; case SymbolKind::Class: if (Lang == SymbolLanguage::ObjC) return CXIdxEntity_ObjCClass; return CXIdxEntity_CXXClass; case SymbolKind::Protocol: if (Lang == SymbolLanguage::ObjC) return CXIdxEntity_ObjCProtocol; return CXIdxEntity_CXXInterface; case SymbolKind::Extension: return CXIdxEntity_ObjCCategory; case SymbolKind::InstanceMethod: if (Lang == SymbolLanguage::ObjC) return CXIdxEntity_ObjCInstanceMethod; return CXIdxEntity_CXXInstanceMethod; case SymbolKind::ClassMethod: return CXIdxEntity_ObjCClassMethod; case SymbolKind::StaticMethod: return CXIdxEntity_CXXStaticMethod; case SymbolKind::InstanceProperty: return CXIdxEntity_ObjCProperty; case SymbolKind::StaticProperty: return CXIdxEntity_CXXStaticVariable; case SymbolKind::Namespace: return CXIdxEntity_CXXNamespace; case SymbolKind::NamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; case SymbolKind::Constructor: return CXIdxEntity_CXXConstructor; case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; case SymbolKind::Parameter: return CXIdxEntity_Variable; case SymbolKind::Concept: return CXIdxEntity_CXXConcept; } llvm_unreachable("invalid symbol kind"); } static CXIdxEntityCXXTemplateKind getEntityKindFromSymbolProperties(SymbolPropertySet K) { if (K & (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization) return CXIdxEntity_TemplatePartialSpecialization; if (K & (SymbolPropertySet)SymbolProperty::TemplateSpecialization) return CXIdxEntity_TemplateSpecialization; if (K & (SymbolPropertySet)SymbolProperty::Generic) return CXIdxEntity_Template; return CXIdxEntity_NonTemplate; } static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { switch (L) { case SymbolLanguage::C: return CXIdxEntityLang_C; case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; case SymbolLanguage::Swift: return CXIdxEntityLang_Swift; } llvm_unreachable("invalid symbol language"); }