//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// // // 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 "clang/Index/IndexSymbol.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Lex/MacroInfo.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).starts_with("test")) return false; return isUnitTestCase(D->getClassInterface()); } static void checkForIBOutlets(const Decl *D, SymbolPropertySet &PropSet) { if (D->hasAttr()) { PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; } else if (D->hasAttr()) { PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; PropSet |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; } } bool index::isFunctionLocalSymbol(const Decl *D) { assert(D); if (isa(D)) return true; if (isa(D)) return true; if (isa(D)) return false; if (!D->getParentFunctionOrMethod()) return false; if (const NamedDecl *ND = dyn_cast(D)) { switch (ND->getFormalLinkage()) { case Linkage::Invalid: llvm_unreachable("Linkage hasn't been computed!"); case Linkage::None: case Linkage::Internal: return true; case Linkage::VisibleNone: case Linkage::UniqueExternal: llvm_unreachable("Not a sema linkage"); case Linkage::Module: case Linkage::External: return false; } } return true; } SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; Info.Kind = SymbolKind::Unknown; Info.SubKind = SymbolSubKind::None; Info.Properties = SymbolPropertySet(); Info.Lang = SymbolLanguage::C; if (isFunctionLocalSymbol(D)) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Local; } if (isa(D->getDeclContext())) { Info.Properties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; } if (auto *VT = dyn_cast(D)) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Lang = SymbolLanguage::CXX; // All other fields are filled from the templated decl. D = VT->getTemplatedDecl(); } if (const TagDecl *TD = dyn_cast(D)) { switch (TD->getTagKind()) { case TagTypeKind::Struct: Info.Kind = SymbolKind::Struct; break; case TagTypeKind::Union: Info.Kind = SymbolKind::Union; break; case TagTypeKind::Class: Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::CXX; break; case TagTypeKind::Interface: Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::CXX; break; case TagTypeKind::Enum: Info.Kind = SymbolKind::Enum; break; } if (const CXXRecordDecl *CXXRec = dyn_cast(D)) { if (!CXXRec->isCLike()) { Info.Lang = SymbolLanguage::CXX; if (CXXRec->getDescribedClassTemplate()) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; } } } if (isa(D)) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Properties |= (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; } else if (isa(D)) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Properties |= (SymbolPropertySet)SymbolProperty::TemplateSpecialization; } } else if (auto *VD = dyn_cast(D)) { Info.Kind = SymbolKind::Variable; if (isa(D)) { Info.Kind = SymbolKind::Parameter; } else if (isa(D->getDeclContext())) { Info.Kind = SymbolKind::StaticProperty; Info.Lang = SymbolLanguage::CXX; } if (isa(D)) { Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Properties |= (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; } else if (isa(D)) { Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Properties |= (SymbolPropertySet)SymbolProperty::TemplateSpecialization; } else if (VD->getDescribedVarTemplate()) { Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; } } 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::Field: case Decl::IndirectField: 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.Properties |= (SymbolPropertySet)SymbolProperty::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; const ObjCInterfaceDecl *ClsD = nullptr; if (auto *CatD = dyn_cast(D)) ClsD = CatD->getClassInterface(); else ClsD = cast(D)->getClassInterface(); if (isUnitTestCase(ClsD)) Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; break; } case Decl::ObjCMethod: { const ObjCMethodDecl *MD = cast(D); Info.Kind = MD->isInstanceMethod() ? SymbolKind::InstanceMethod : SymbolKind::ClassMethod; if (MD->isPropertyAccessor()) { if (MD->param_size()) Info.SubKind = SymbolSubKind::AccessorSetter; else Info.SubKind = SymbolSubKind::AccessorGetter; } Info.Lang = SymbolLanguage::ObjC; if (isUnitTest(MD)) Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; if (D->hasAttr()) Info.Properties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; break; } case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; checkForIBOutlets(D, Info.Properties); if (auto *Annot = D->getAttr()) { if (Annot->getAnnotation() == "gk_inspectable") Info.Properties |= (SymbolPropertySet)SymbolProperty::GKInspectable; } break; case Decl::ObjCIvar: Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; checkForIBOutlets(D, Info.Properties); 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; auto *CD = cast(D); if (CD->isCopyConstructor()) Info.SubKind = SymbolSubKind::CXXCopyConstructor; else if (CD->isMoveConstructor()) Info.SubKind = SymbolSubKind::CXXMoveConstructor; 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.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; Info.Properties |= (SymbolPropertySet)SymbolProperty::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.Properties |= (SymbolPropertySet)SymbolProperty::Generic; break; case Decl::TypeAlias: Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; break; case Decl::UnresolvedUsingTypename: Info.Kind = SymbolKind::Using; Info.SubKind = SymbolSubKind::UsingTypename; Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; break; case Decl::UnresolvedUsingValue: Info.Kind = SymbolKind::Using; Info.SubKind = SymbolSubKind::UsingValue; Info.Lang = SymbolLanguage::CXX; Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; break; case Decl::Using: Info.Kind = SymbolKind::Using; Info.Lang = SymbolLanguage::CXX; break; case Decl::UsingEnum: Info.Kind = SymbolKind::Using; Info.Lang = SymbolLanguage::CXX; Info.SubKind = SymbolSubKind::UsingEnum; break; case Decl::Binding: Info.Kind = SymbolKind::Variable; Info.Lang = SymbolLanguage::CXX; break; case Decl::MSProperty: Info.Kind = SymbolKind::InstanceProperty; if (const CXXRecordDecl *CXXRec = dyn_cast(D->getDeclContext())) { if (!CXXRec->isCLike()) Info.Lang = SymbolLanguage::CXX; } break; case Decl::ClassTemplatePartialSpecialization: case Decl::ClassTemplateSpecialization: case Decl::CXXRecord: case Decl::Enum: case Decl::Record: llvm_unreachable("records handled before"); break; case Decl::VarTemplateSpecialization: case Decl::VarTemplatePartialSpecialization: case Decl::ImplicitParam: case Decl::ParmVar: case Decl::Var: case Decl::VarTemplate: llvm_unreachable("variables handled before"); break; case Decl::TemplateTypeParm: Info.Kind = SymbolKind::TemplateTypeParm; break; case Decl::TemplateTemplateParm: Info.Kind = SymbolKind::TemplateTemplateParm; break; case Decl::NonTypeTemplateParm: Info.Kind = SymbolKind::NonTypeTemplateParm; break; case Decl::Concept: Info.Kind = SymbolKind::Concept; break; // Other decls get the 'unknown' kind. default: break; } } if (Info.Kind == SymbolKind::Unknown) return Info; if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplateSpecialization) { Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; Info.Properties |= (SymbolPropertySet)SymbolProperty::TemplateSpecialization; } } if (Info.Properties & (SymbolPropertySet)SymbolProperty::Generic) Info.Lang = SymbolLanguage::CXX; if (auto *attr = D->getExternalSourceSymbolAttr()) { if (attr->getLanguage() == "Swift") Info.Lang = SymbolLanguage::Swift; } return Info; } SymbolInfo index::getSymbolInfoForMacro(const MacroInfo &) { SymbolInfo Info; Info.Kind = SymbolKind::Macro; Info.SubKind = SymbolSubKind::None; Info.Properties = SymbolPropertySet(); Info.Lang = SymbolLanguage::C; return Info; } bool index::applyForEachSymbolRoleInterruptible(SymbolRoleSet Roles, llvm::function_ref Fn) { #define APPLY_FOR_ROLE(Role) \ if (Roles & (unsigned)SymbolRole::Role) \ if (!Fn(SymbolRole::Role)) \ return false; 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(Undefinition); APPLY_FOR_ROLE(RelationChildOf); APPLY_FOR_ROLE(RelationBaseOf); APPLY_FOR_ROLE(RelationOverrideOf); APPLY_FOR_ROLE(RelationReceivedBy); APPLY_FOR_ROLE(RelationCalledBy); APPLY_FOR_ROLE(RelationExtendedBy); APPLY_FOR_ROLE(RelationAccessorOf); APPLY_FOR_ROLE(RelationContainedBy); APPLY_FOR_ROLE(RelationIBTypeOf); APPLY_FOR_ROLE(RelationSpecializationOf); APPLY_FOR_ROLE(NameReference); #undef APPLY_FOR_ROLE return true; } void index::applyForEachSymbolRole(SymbolRoleSet Roles, llvm::function_ref Fn) { applyForEachSymbolRoleInterruptible(Roles, [&](SymbolRole r) -> bool { Fn(r); return true; }); } 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::Undefinition: OS << "Undef"; 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; case SymbolRole::RelationExtendedBy: OS << "RelExt"; break; case SymbolRole::RelationAccessorOf: OS << "RelAcc"; break; case SymbolRole::RelationContainedBy: OS << "RelCont"; break; case SymbolRole::RelationIBTypeOf: OS << "RelIBType"; break; case SymbolRole::RelationSpecializationOf: OS << "RelSpecialization"; break; case SymbolRole::NameReference: OS << "NameReference"; 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 "conversion-func"; case SymbolKind::Parameter: return "param"; case SymbolKind::Using: return "using"; case SymbolKind::TemplateTypeParm: return "template-type-param"; case SymbolKind::TemplateTemplateParm: return "template-template-param"; case SymbolKind::NonTypeTemplateParm: return "non-type-template-param"; case SymbolKind::Concept: return "concept"; } llvm_unreachable("invalid symbol kind"); } StringRef index::getSymbolSubKindString(SymbolSubKind K) { switch (K) { case SymbolSubKind::None: return ""; case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor"; case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; case SymbolSubKind::AccessorGetter: return "acc-get"; case SymbolSubKind::AccessorSetter: return "acc-set"; case SymbolSubKind::UsingTypename: return "using-typename"; case SymbolSubKind::UsingValue: return "using-value"; case SymbolSubKind::UsingEnum: return "using-enum"; } llvm_unreachable("invalid symbol subkind"); } StringRef index::getSymbolLanguageString(SymbolLanguage K) { switch (K) { case SymbolLanguage::C: return "C"; case SymbolLanguage::ObjC: return "ObjC"; case SymbolLanguage::CXX: return "C++"; case SymbolLanguage::Swift: return "Swift"; } llvm_unreachable("invalid symbol language kind"); } void index::applyForEachSymbolProperty(SymbolPropertySet Props, llvm::function_ref Fn) { #define APPLY_FOR_PROPERTY(K) \ if (Props & (SymbolPropertySet)SymbolProperty::K) \ Fn(SymbolProperty::K) APPLY_FOR_PROPERTY(Generic); APPLY_FOR_PROPERTY(TemplatePartialSpecialization); APPLY_FOR_PROPERTY(TemplateSpecialization); APPLY_FOR_PROPERTY(UnitTest); APPLY_FOR_PROPERTY(IBAnnotated); APPLY_FOR_PROPERTY(IBOutletCollection); APPLY_FOR_PROPERTY(GKInspectable); APPLY_FOR_PROPERTY(Local); APPLY_FOR_PROPERTY(ProtocolInterface); #undef APPLY_FOR_PROPERTY } void index::printSymbolProperties(SymbolPropertySet Props, raw_ostream &OS) { bool VisitedOnce = false; applyForEachSymbolProperty(Props, [&](SymbolProperty Prop) { if (VisitedOnce) OS << ','; else VisitedOnce = true; switch (Prop) { case SymbolProperty::Generic: OS << "Gen"; break; case SymbolProperty::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolProperty::TemplateSpecialization: OS << "TS"; break; case SymbolProperty::UnitTest: OS << "test"; break; case SymbolProperty::IBAnnotated: OS << "IB"; break; case SymbolProperty::IBOutletCollection: OS << "IBColl"; break; case SymbolProperty::GKInspectable: OS << "GKI"; break; case SymbolProperty::Local: OS << "local"; break; case SymbolProperty::ProtocolInterface: OS << "protocol"; break; } }); }