Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -14,6 +14,7 @@ #include "../URI.h" #include "CanonicalIncludes.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceManager.h" #include "clang/Index/IndexSymbol.h" @@ -26,6 +27,19 @@ namespace clangd { namespace { +/// If \p ND is a template specialization, returns the primary template. +/// Otherwise, returns \p ND. +const NamedDecl &getTemplateOrThis(const NamedDecl &ND) { + if (auto Cls = dyn_cast(&ND)) { + if (auto T = Cls->getDescribedTemplate()) + return *T; + } else if (auto Func = dyn_cast(&ND)) { + if (auto T = Func->getPrimaryTemplate()) + return *T; + } + return ND; +} + // Returns a URI of \p Path. Firstly, this makes the \p Path absolute using the // current working directory of the given SourceManager if the Path is not an // absolute path. If failed, this resolves relative paths against \p FallbackDir @@ -325,7 +339,8 @@ // Add completion info. // FIXME: we may want to choose a different redecl, or combine from several. assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set."); - CodeCompletionResult SymbolCompletion(&ND, 0); + // We use the primary template, as clang does during code completion. + CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0); const auto *CCS = SymbolCompletion.CreateCodeCompletionString( *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator, *CompletionTUInfo, Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -195,6 +195,44 @@ EXPECT_TRUE(SeenSymbol); } +TEST(FileIndexTest, TemplateParamsInLabel) { + auto Source = R"cpp( +template +class vector { +}; + +template +vector make_vector(Ty* begin, Ty* end) {} +)cpp"; + + FileIndex M; + M.update("f", build("f", Source).getPointer()); + + FuzzyFindRequest Req; + Req.Query = ""; + bool SeenVector = false; + bool SeenMakeVector = false; + M.fuzzyFind(Req, [&](const Symbol &Sym) { + if (Sym.Name == "vector") { + EXPECT_EQ(Sym.CompletionLabel, "vector"); + EXPECT_EQ(Sym.CompletionSnippetInsertText, "vector<${1:class Ty}>"); + EXPECT_EQ(Sym.CompletionPlainInsertText, "vector"); + SeenVector = true; + return; + } + + if (Sym.Name == "make_vector") { + EXPECT_EQ(Sym.CompletionLabel, "make_vector(Ty *begin, Ty *end)"); + EXPECT_EQ(Sym.CompletionSnippetInsertText, + "make_vector(${1:Ty *begin}, ${2:Ty *end})"); + EXPECT_EQ(Sym.CompletionPlainInsertText, "make_vector"); + SeenMakeVector = true; + } + }); + EXPECT_TRUE(SeenVector); + EXPECT_TRUE(SeenMakeVector); +} + } // namespace } // namespace clangd } // namespace clang