Index: include/clang-c/Index.h =================================================================== --- include/clang-c/Index.h +++ include/clang-c/Index.h @@ -2578,6 +2578,31 @@ CXCursor_OverloadCandidate = 700 }; +/** + * \brief Describes the kind of template specialization a cursor refers to. + */ +enum CXTemplateSpecializationKind { + + /// This template specialization was formed from a template-id but + /// has not yet been declared, defined, or instantiated. + CXTSK_Undeclared = 0, + /// This template specialization was implicitly instantiated from a + /// template. (C++ [temp.inst]). + CXTSK_ImplicitInstantiation, + /// This template specialization was declared or defined by an + /// explicit specialization (C++ [temp.expl.spec]) or partial + /// specialization (C++ [temp.class.spec]). + CXTSK_ExplicitSpecialization, + /// This template specialization was instantiated from a template + /// due to an explicit instantiation declaration request + /// (C++11 [temp.explicit]). + CXTSK_ExplicitInstantiationDeclaration, + /// This template specialization was instantiated from a template + /// due to an explicit instantiation definition request + /// (C++ [temp.explicit]). + CXTSK_ExplicitInstantiationDefinition +}; + /** * \brief A cursor representing some element in the abstract syntax tree for * a translation unit. @@ -4599,6 +4624,24 @@ * \c CXCursor_NoDeclFound. */ CINDEX_LINKAGE enum CXCursorKind clang_getTemplateCursorKind(CXCursor C); + +/** + * \brief Given a cursor that represents a template, determine the kind + * of specialization (e.g. implicit or explicit instantiation) + * + * This routine can be used to eliminate duplicate explicit template + * instantiations that occur both as a child of the template, and at + * the place where they got explicitly instantiated. By ignoring explicit + * instantiations which parent is / isn't the template itself, no + * duplicates will occur. + * + * \param C The cursor to query. This cursor should represent a template + * declaration. + * + * \returns The template specialization kind. If \p C is not a template, returns + * \c CXTSK_Undeclared. + */ +CINDEX_LINKAGE enum CXTemplateSpecializationKind clang_getTemplateSpecializationKind(CXCursor C); /** * \brief Given a cursor that may represent a specialization or instantiation Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -3222,6 +3222,8 @@ case Decl::AccessSpec: return CXCursor_CXXAccessSpecifier; case Decl::ClassTemplatePartialSpecialization: return CXCursor_ClassTemplatePartialSpecialization; + case Decl::ClassTemplateSpecialization: + return CXCursor_ClassTemplateSpecialization; case Decl::UsingDirective: return CXCursor_UsingDirective; case Decl::StaticAssert: return CXCursor_StaticAssert; case Decl::Friend: return CXCursor_FriendDecl; Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -498,7 +498,13 @@ if (!D) return false; - return VisitAttributes(D) || Visit(D); + if (VisitAttributes(D)) + return true; + + if (Cursor.kind == CXCursor_ClassTemplateSpecialization) + return VisitClassTemplateSpecializationDecl(static_cast(D), /*EnforceVisitBody*/ true); + else + return Visit(D); } if (clang_isStatement(Cursor.kind)) { @@ -701,22 +707,23 @@ } bool CursorVisitor::VisitClassTemplateSpecializationDecl( - ClassTemplateSpecializationDecl *D) { - bool ShouldVisitBody = false; - switch (D->getSpecializationKind()) { - case TSK_Undeclared: - case TSK_ImplicitInstantiation: - // Nothing to visit - return false; - - case TSK_ExplicitInstantiationDeclaration: - case TSK_ExplicitInstantiationDefinition: - break; - - case TSK_ExplicitSpecialization: - ShouldVisitBody = true; - break; - } + ClassTemplateSpecializationDecl *D, bool EnforceVisitBody) { + bool ShouldVisitBody = EnforceVisitBody; + if (!EnforceVisitBody) + switch (D->getSpecializationKind()) { + case TSK_Undeclared: + case TSK_ImplicitInstantiation: + // Nothing to visit + return false; + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + break; + + case TSK_ExplicitSpecialization: + ShouldVisitBody = true; + break; + } // Visit the template arguments used in the specialization. if (TypeSourceInfo *SpecType = D->getTypeAsWritten()) { @@ -939,7 +946,18 @@ return true; auto* CD = D->getTemplatedDecl(); - return VisitAttributes(CD) || VisitCXXRecordDecl(CD); + if (VisitAttributes(CD)) + return true; + if (VisitCXXRecordDecl(CD)) + return true; + + for (auto *Child : D->specializations()) + if (Visit(MakeCXCursor(Child, TU))) + return true; // FIXME do this for function and var templates too. + // inspired by tools/clang/lib/AST/ASTDumper.cpp, ASTDumper::VisitTemplateDecl + // which is called from VisitClassTemplateDecl etc. + + return false; } bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { Index: tools/libclang/CIndexCXX.cpp =================================================================== --- tools/libclang/CIndexCXX.cpp +++ tools/libclang/CIndexCXX.cpp @@ -80,6 +80,25 @@ return CXCursor_NoDeclFound; } +enum CXTemplateSpecializationKind clang_getTemplateSpecializationKind(CXCursor C) { + using namespace clang::cxcursor; + + if (C.kind == CXCursor_ClassTemplateSpecialization) + { + switch (static_cast(getCursorDecl(C))->getSpecializationKind()) + { + #define TRANSL(val) case val: return CX##val; + TRANSL(TSK_Undeclared) + TRANSL(TSK_ImplicitInstantiation) + TRANSL(TSK_ExplicitSpecialization) + TRANSL(TSK_ExplicitInstantiationDeclaration) + TRANSL(TSK_ExplicitInstantiationDefinition) + #undef TRANSL + } + } + return CXTSK_Undeclared; +} + CXCursor clang_getSpecializedCursorTemplate(CXCursor C) { if (!clang_isDeclaration(C.kind)) return clang_getNullCursor(); Index: tools/libclang/CursorVisitor.h =================================================================== --- tools/libclang/CursorVisitor.h +++ tools/libclang/CursorVisitor.h @@ -205,7 +205,7 @@ bool VisitTranslationUnitDecl(TranslationUnitDecl *D); bool VisitTypedefDecl(TypedefDecl *D); bool VisitTagDecl(TagDecl *D); - bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D); + bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D, bool EnforceVisitBody = false); bool VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D); bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D); Index: tools/libclang/libclang.exports =================================================================== --- tools/libclang/libclang.exports +++ tools/libclang/libclang.exports @@ -259,6 +259,7 @@ clang_getSpellingLocation clang_getTUResourceUsageName clang_getTemplateCursorKind +clang_getTemplateSpecializationKind clang_getTokenExtent clang_getTokenKind clang_getTokenLocation