//===- IndexDecl.cpp - Indexing declarations ------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "IndexingContext.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclVisitor.h" using namespace clang; using namespace index; #define TRY_TO(CALL_EXPR) \ do { \ if (!CALL_EXPR) \ return false; \ } while (0) namespace { class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> { IndexingContext &IndexCtx; public: explicit IndexingDeclVisitor(IndexingContext &indexCtx) : IndexCtx(indexCtx) { } bool Handled = true; bool VisitDecl(const Decl *D) { Handled = false; return true; } /// \brief Returns true if the given method has been defined explicitly by the /// user. static bool hasUserDefined(const ObjCMethodDecl *D, const ObjCImplDecl *Container) { const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), D->isInstanceMethod()); return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition(); } void handleDeclarator(const DeclaratorDecl *D, const NamedDecl *Parent = nullptr) { if (!Parent) Parent = D; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); if (IndexCtx.shouldIndexFunctionLocalSymbols()) { // Only index parameters in definitions, parameters in declarations are // not useful. if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { auto *DC = Parm->getDeclContext(); if (auto *FD = dyn_cast<FunctionDecl>(DC)) { if (FD->isThisDeclarationADefinition()) IndexCtx.handleDecl(Parm); } else if (auto *MD = dyn_cast<ObjCMethodDecl>(DC)) { if (MD->isThisDeclarationADefinition()) IndexCtx.handleDecl(Parm); } else { IndexCtx.handleDecl(Parm); } } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (FD->isThisDeclarationADefinition()) { for (auto PI : FD->parameters()) { IndexCtx.handleDecl(PI); } } } } } bool handleObjCMethod(const ObjCMethodDecl *D) { if (!IndexCtx.handleDecl(D, (unsigned)SymbolRole::Dynamic)) return false; IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); for (const auto *I : D->parameters()) handleDeclarator(I, D); if (D->isThisDeclarationADefinition()) { const Stmt *Body = D->getBody(); if (Body) { IndexCtx.indexBody(Body, D, D); } } return true; } bool VisitFunctionDecl(const FunctionDecl *D) { if (D->isDeleted()) return true; SymbolRoleSet Roles{}; SmallVector<SymbolRelation, 4> Relations; if (auto *CXXMD = dyn_cast<CXXMethodDecl>(D)) { if (CXXMD->isVirtual()) Roles |= (unsigned)SymbolRole::Dynamic; for (auto I = CXXMD->begin_overridden_methods(), E = CXXMD->end_overridden_methods(); I != E; ++I) { Relations.emplace_back((unsigned)SymbolRole::RelationOverrideOf, *I); } } if (!IndexCtx.handleDecl(D, Roles, Relations)) return false; handleDeclarator(D); if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) { // Constructor initializers. for (const auto *Init : Ctor->inits()) { if (Init->isWritten()) { IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); if (const FieldDecl *Member = Init->getAnyMember()) IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D, (unsigned)SymbolRole::Write); IndexCtx.indexBody(Init->getInit(), D, D); } } } if (D->isThisDeclarationADefinition()) { const Stmt *Body = D->getBody(); if (Body) { IndexCtx.indexBody(Body, D, D); } } return true; } bool VisitVarDecl(const VarDecl *D) { if (!IndexCtx.handleDecl(D)) return false; handleDeclarator(D); IndexCtx.indexBody(D->getInit(), D); return true; } bool VisitFieldDecl(const FieldDecl *D) { if (!IndexCtx.handleDecl(D)) return false; handleDeclarator(D); if (D->isBitField()) IndexCtx.indexBody(D->getBitWidth(), D); else if (D->hasInClassInitializer()) IndexCtx.indexBody(D->getInClassInitializer(), D); return true; } bool VisitObjCIvarDecl(const ObjCIvarDecl *D) { if (D->getSynthesize()) { // For synthesized ivars, use the location of the ObjC implementation, // not the location of the property. // Otherwise the header file containing the @interface will have different // indexing contents based on whether the @implementation was present or // not in the translation unit. return IndexCtx.handleDecl(D, cast<Decl>(D->getDeclContext())->getLocation(), (unsigned)SymbolRole::Implicit); } if (!IndexCtx.handleDecl(D)) return false; handleDeclarator(D); return true; } bool VisitMSPropertyDecl(const MSPropertyDecl *D) { handleDeclarator(D); return true; } bool VisitEnumConstantDecl(const EnumConstantDecl *D) { if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexBody(D->getInitExpr(), D); return true; } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitTagDecl(const TagDecl *D) { // Non-free standing tags are handled in indexTypeSourceInfo. if (D->isFreeStanding()) { if (D->isThisDeclarationADefinition()) { IndexCtx.indexTagDecl(D); } else { auto *Parent = dyn_cast<NamedDecl>(D->getDeclContext()); return IndexCtx.handleReference(D, D->getLocation(), Parent, D->getLexicalDeclContext(), SymbolRoleSet()); } } return true; } bool handleReferencedProtocols(const ObjCProtocolList &ProtList, const ObjCContainerDecl *ContD) { 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; TRY_TO(IndexCtx.handleReference(PD, Loc, ContD, ContD, SymbolRoleSet(), SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, ContD})); } return true; } bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { if (D->isThisDeclarationADefinition()) { TRY_TO(IndexCtx.handleDecl(D)); if (auto *SuperD = D->getSuperClass()) { TRY_TO(IndexCtx.handleReference(SuperD, D->getSuperClassLoc(), D, D, SymbolRoleSet(), SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, D})); } TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, D->getDeclContext(), SymbolRoleSet()); } return true; } bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { if (D->isThisDeclarationADefinition()) { TRY_TO(IndexCtx.handleDecl(D)); TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, D->getDeclContext(), SymbolRoleSet()); } return true; } bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { const ObjCInterfaceDecl *Class = D->getClassInterface(); if (!Class) return true; if (Class->isImplicitInterfaceDecl()) IndexCtx.handleDecl(Class); if (!IndexCtx.handleDecl(D)) return false; // Index the ivars first to make sure the synthesized ivars are indexed // before indexing the methods that can reference them. for (const auto *IvarI : D->ivars()) IndexCtx.indexDecl(IvarI); for (const auto *I : D->decls()) { if (!isa<ObjCIvarDecl>(I)) IndexCtx.indexDecl(I); } return true; } bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexDeclContext(D); return true; } bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { const ObjCCategoryDecl *Cat = D->getCategoryDecl(); if (!Cat) return true; if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexDeclContext(D); return true; } bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { // Methods associated with a property, even user-declared ones, are // handled when we handle the property. if (D->isPropertyAccessor()) return true; handleObjCMethod(D); return true; } bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { if (ObjCMethodDecl *MD = D->getGetterMethodDecl()) if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) handleObjCMethod(MD); if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) handleObjCMethod(MD); if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); if (!IndexCtx.handleReference(PD, D->getLocation(), /*Parent=*/cast<NamedDecl>(D->getDeclContext()), D->getDeclContext(), SymbolRoleSet(), {}, /*RefE=*/nullptr, D)) return false; if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) return true; assert(D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize); if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { if (!IvarD->getSynthesize()) IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), nullptr, D->getDeclContext(), SymbolRoleSet()); } auto *ImplD = cast<ObjCImplDecl>(D->getDeclContext()); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { if (MD->isPropertyAccessor() && !hasUserDefined(MD, ImplD)) IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { if (MD->isPropertyAccessor() && !hasUserDefined(MD, ImplD)) IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } return true; } bool VisitNamespaceDecl(const NamespaceDecl *D) { if (!IndexCtx.handleDecl(D)) return false; IndexCtx.indexDeclContext(D); return true; } bool VisitUsingDecl(const UsingDecl *D) { const DeclContext *DC = D->getDeclContext()->getRedeclContext(); const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, D->getLexicalDeclContext()); for (const auto *I : D->shadows()) IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), Parent, D->getLexicalDeclContext(), SymbolRoleSet()); return true; } bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { const DeclContext *DC = D->getDeclContext()->getRedeclContext(); const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, D->getLexicalDeclContext()); return IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), D->getLocation(), Parent, D->getLexicalDeclContext(), SymbolRoleSet()); } bool VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { // FIXME: Notify subsequent callbacks if info comes from implicit // instantiation. if (D->isThisDeclarationADefinition()) IndexCtx.indexTagDecl(D); return true; } bool VisitTemplateDecl(const TemplateDecl *D) { // FIXME: Template parameters. return Visit(D->getTemplatedDecl()); } bool VisitFriendDecl(const FriendDecl *D) { if (auto ND = D->getFriendDecl()) { // FIXME: Ignore a class template in a dependent context, these are not // linked properly with their redeclarations, ending up with duplicate // USRs. // See comment "Friend templates are visible in fairly strange ways." in // SemaTemplate.cpp which precedes code that prevents the friend template // from becoming visible from the enclosing context. if (isa<ClassTemplateDecl>(ND) && D->getDeclContext()->isDependentContext()) return true; return Visit(ND); } if (auto Ty = D->getFriendType()) { IndexCtx.indexTypeSourceInfo(Ty, cast<NamedDecl>(D->getDeclContext())); } return true; } bool VisitImportDecl(const ImportDecl *D) { return IndexCtx.importedModule(D); } }; } // anonymous namespace bool IndexingContext::indexDecl(const Decl *D) { if (D->isImplicit() && shouldIgnoreIfImplicit(D)) return true; if (isTemplateImplicitInstantiation(D)) return true; IndexingDeclVisitor Visitor(*this); bool ShouldContinue = Visitor.Visit(D); if (!ShouldContinue) return false; if (!Visitor.Handled && isa<DeclContext>(D)) return indexDeclContext(cast<DeclContext>(D)); return true; } bool IndexingContext::indexDeclContext(const DeclContext *DC) { for (const auto *I : DC->decls()) if (!indexDecl(I)) return false; return true; } bool IndexingContext::indexTopLevelDecl(const Decl *D) { if (D->getLocation().isInvalid()) return true; if (isa<ObjCMethodDecl>(D)) return true; // Wait for the objc container. return indexDecl(D); } bool IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) if (!indexTopLevelDecl(*I)) return false; return true; }