//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "clang/Index/IndexSymbol.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyPrinter.h" using namespace clang; using namespace clang::index; /// \returns true if \c D is a subclass of 'XCTestCase'. static bool isUnitTestCase(const ObjCInterfaceDecl *D) { if (!D) return false; while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) { if (SuperD->getName() == "XCTestCase") return true; D = SuperD; } return false; } /// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has /// no parameters, and its name starts with 'test'. static bool isUnitTest(const ObjCMethodDecl *D) { if (!D->parameters().empty()) return false; if (!D->getReturnType()->isVoidType()) return false; if (!D->getSelector().getNameForSlot(0).startswith("test")) return false; return isUnitTestCase(D->getClassInterface()); } static void checkForIBOutlets(const Decl *D, SymbolSubKindSet &SubKindSet) { if (D->hasAttr()) { SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; } else if (D->hasAttr()) { SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; SubKindSet |= (unsigned)SymbolSubKind::IBOutletCollection; } } SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; Info.Kind = SymbolKind::Unknown; Info.SubKinds = SymbolSubKindSet(); Info.Lang = SymbolLanguage::C; if (const TagDecl *TD = dyn_cast(D)) { switch (TD->getTagKind()) { case TTK_Struct: Info.Kind = SymbolKind::Struct; break; case TTK_Union: Info.Kind = SymbolKind::Union; break; case TTK_Class: Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::CXX; break; case TTK_Interface: Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::CXX; break; case TTK_Enum: Info.Kind = SymbolKind::Enum; break; } if (const CXXRecordDecl *CXXRec = dyn_cast(D)) if (!CXXRec->isCLike()) Info.Lang = SymbolLanguage::CXX; if (isa(D)) { Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.SubKinds |= (unsigned)SymbolSubKind::TemplatePartialSpecialization; } else if (isa(D)) { Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; } } else { switch (D->getKind()) { case Decl::Import: Info.Kind = SymbolKind::Module; break; case Decl::Typedef: Info.Kind = SymbolKind::TypeAlias; break; // Lang = C case Decl::Function: Info.Kind = SymbolKind::Function; break; case Decl::ParmVar: Info.Kind = SymbolKind::Variable; break; case Decl::Var: Info.Kind = SymbolKind::Variable; if (isa(D->getDeclContext())) { Info.Kind = SymbolKind::StaticProperty; Info.Lang = SymbolLanguage::CXX; } break; case Decl::Field: Info.Kind = SymbolKind::Field; if (const CXXRecordDecl * CXXRec = dyn_cast(D->getDeclContext())) { if (!CXXRec->isCLike()) Info.Lang = SymbolLanguage::CXX; } break; case Decl::EnumConstant: Info.Kind = SymbolKind::EnumConstant; break; case Decl::ObjCInterface: case Decl::ObjCImplementation: { Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::ObjC; const ObjCInterfaceDecl *ClsD = dyn_cast(D); if (!ClsD) ClsD = cast(D)->getClassInterface(); if (isUnitTestCase(ClsD)) Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; break; } case Decl::ObjCProtocol: Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCCategory: case Decl::ObjCCategoryImpl: Info.Kind = SymbolKind::Extension; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCMethod: if (cast(D)->isInstanceMethod()) Info.Kind = SymbolKind::InstanceMethod; else Info.Kind = SymbolKind::ClassMethod; Info.Lang = SymbolLanguage::ObjC; if (isUnitTest(cast(D))) Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; if (D->hasAttr()) Info.SubKinds |= (unsigned)SymbolSubKind::IBAnnotated; break; case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; checkForIBOutlets(D, Info.SubKinds); break; case Decl::ObjCIvar: Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; checkForIBOutlets(D, Info.SubKinds); break; case Decl::Namespace: Info.Kind = SymbolKind::Namespace; Info.Lang = SymbolLanguage::CXX; break; case Decl::NamespaceAlias: Info.Kind = SymbolKind::NamespaceAlias; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConstructor: Info.Kind = SymbolKind::Constructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXDestructor: Info.Kind = SymbolKind::Destructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConversion: Info.Kind = SymbolKind::ConversionFunction; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXMethod: { const CXXMethodDecl *MD = cast(D); if (MD->isStatic()) Info.Kind = SymbolKind::StaticMethod; else Info.Kind = SymbolKind::InstanceMethod; Info.Lang = SymbolLanguage::CXX; break; } case Decl::ClassTemplate: Info.Kind = SymbolKind::Class; Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; if (const CXXMethodDecl *MD = dyn_cast_or_null( cast(D)->getTemplatedDecl())) { if (isa(MD)) Info.Kind = SymbolKind::Constructor; else if (isa(MD)) Info.Kind = SymbolKind::Destructor; else if (isa(MD)) Info.Kind = SymbolKind::ConversionFunction; else { if (MD->isStatic()) Info.Kind = SymbolKind::StaticMethod; else Info.Kind = SymbolKind::InstanceMethod; } } break; case Decl::TypeAliasTemplate: Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; Info.SubKinds |= (unsigned)SymbolSubKind::Generic; break; case Decl::TypeAlias: Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; break; default: break; } } if (Info.Kind == SymbolKind::Unknown) return Info; if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplateSpecialization) { Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; } } if (Info.SubKinds & (unsigned)SymbolSubKind::Generic) Info.Lang = SymbolLanguage::CXX; return Info; } void index::applyForEachSymbolRole(SymbolRoleSet Roles, llvm::function_ref Fn) { #define APPLY_FOR_ROLE(Role) \ if (Roles & (unsigned)SymbolRole::Role) \ Fn(SymbolRole::Role) APPLY_FOR_ROLE(Declaration); APPLY_FOR_ROLE(Definition); APPLY_FOR_ROLE(Reference); APPLY_FOR_ROLE(Read); APPLY_FOR_ROLE(Write); APPLY_FOR_ROLE(Call); APPLY_FOR_ROLE(Dynamic); APPLY_FOR_ROLE(AddressOf); APPLY_FOR_ROLE(Implicit); APPLY_FOR_ROLE(RelationChildOf); APPLY_FOR_ROLE(RelationBaseOf); APPLY_FOR_ROLE(RelationOverrideOf); APPLY_FOR_ROLE(RelationReceivedBy); APPLY_FOR_ROLE(RelationCalledBy); #undef APPLY_FOR_ROLE } void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { bool VisitedOnce = false; applyForEachSymbolRole(Roles, [&](SymbolRole Role) { if (VisitedOnce) OS << ','; else VisitedOnce = true; switch (Role) { case SymbolRole::Declaration: OS << "Decl"; break; case SymbolRole::Definition: OS << "Def"; break; case SymbolRole::Reference: OS << "Ref"; break; case SymbolRole::Read: OS << "Read"; break; case SymbolRole::Write: OS << "Writ"; break; case SymbolRole::Call: OS << "Call"; break; case SymbolRole::Dynamic: OS << "Dyn"; break; case SymbolRole::AddressOf: OS << "Addr"; break; case SymbolRole::Implicit: OS << "Impl"; break; case SymbolRole::RelationChildOf: OS << "RelChild"; break; case SymbolRole::RelationBaseOf: OS << "RelBase"; break; case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; case SymbolRole::RelationCalledBy: OS << "RelCall"; break; } }); } bool index::printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS) { if (auto *ND = dyn_cast(D)) { PrintingPolicy Policy(LO); // Forward references can have different template argument names. Suppress // the template argument names in constructors to make their name more // stable. Policy.SuppressTemplateArgsInCXXConstructors = true; DeclarationName DeclName = ND->getDeclName(); if (DeclName.isEmpty()) return true; DeclName.print(OS, Policy); return false; } else { return true; } } StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { case SymbolKind::Unknown: return ""; case SymbolKind::Module: return "module"; case SymbolKind::Namespace: return "namespace"; case SymbolKind::NamespaceAlias: return "namespace-alias"; case SymbolKind::Macro: return "macro"; case SymbolKind::Enum: return "enum"; case SymbolKind::Struct: return "struct"; case SymbolKind::Class: return "class"; case SymbolKind::Protocol: return "protocol"; case SymbolKind::Extension: return "extension"; case SymbolKind::Union: return "union"; case SymbolKind::TypeAlias: return "type-alias"; case SymbolKind::Function: return "function"; case SymbolKind::Variable: return "variable"; case SymbolKind::Field: return "field"; case SymbolKind::EnumConstant: return "enumerator"; case SymbolKind::InstanceMethod: return "instance-method"; case SymbolKind::ClassMethod: return "class-method"; case SymbolKind::StaticMethod: return "static-method"; case SymbolKind::InstanceProperty: return "instance-property"; case SymbolKind::ClassProperty: return "class-property"; case SymbolKind::StaticProperty: return "static-property"; case SymbolKind::Constructor: return "constructor"; case SymbolKind::Destructor: return "destructor"; case SymbolKind::ConversionFunction: return "coversion-func"; } llvm_unreachable("invalid symbol kind"); } StringRef index::getSymbolLanguageString(SymbolLanguage K) { switch (K) { case SymbolLanguage::C: return "C"; case SymbolLanguage::ObjC: return "ObjC"; case SymbolLanguage::CXX: return "C++"; } llvm_unreachable("invalid symbol language kind"); } void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, llvm::function_ref Fn) { #define APPLY_FOR_SUBKIND(K) \ if (SubKinds & (unsigned)SymbolSubKind::K) \ Fn(SymbolSubKind::K) APPLY_FOR_SUBKIND(Generic); APPLY_FOR_SUBKIND(TemplatePartialSpecialization); APPLY_FOR_SUBKIND(TemplateSpecialization); APPLY_FOR_SUBKIND(UnitTest); APPLY_FOR_SUBKIND(IBAnnotated); APPLY_FOR_SUBKIND(IBOutletCollection); #undef APPLY_FOR_SUBKIND } void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { bool VisitedOnce = false; applyForEachSymbolSubKind(SubKinds, [&](SymbolSubKind SubKind) { if (VisitedOnce) OS << ','; else VisitedOnce = true; switch (SubKind) { case SymbolSubKind::Generic: OS << "Gen"; break; case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; case SymbolSubKind::UnitTest: OS << "test"; break; case SymbolSubKind::IBAnnotated: OS << "IB"; break; case SymbolSubKind::IBOutletCollection: OS << "IBColl"; break; } }); }