diff --git a/clang-tools-extra/clangd/index/Serialization.cpp b/clang-tools-extra/clangd/index/Serialization.cpp --- a/clang-tools-extra/clangd/index/Serialization.cpp +++ b/clang-tools-extra/clangd/index/Serialization.cpp @@ -282,6 +282,7 @@ OS.write(static_cast(Sym.SymInfo.Lang)); writeVar(Strings.index(Sym.Name), OS); writeVar(Strings.index(Sym.Scope), OS); + writeVar(Strings.index(Sym.TemplateSpecializationArgs), OS); writeLocation(Sym.Definition, Strings, OS); writeLocation(Sym.CanonicalDeclaration, Strings, OS); writeVar(Sym.References, OS); @@ -309,6 +310,7 @@ Sym.SymInfo.Lang = static_cast(Data.consume8()); Sym.Name = Data.consumeString(Strings); Sym.Scope = Data.consumeString(Strings); + Sym.TemplateSpecializationArgs = Data.consumeString(Strings); Sym.Definition = readLocation(Data, Strings); Sym.CanonicalDeclaration = readLocation(Data, Strings); Sym.References = Data.consumeVar(); diff --git a/clang-tools-extra/clangd/index/Symbol.h b/clang-tools-extra/clangd/index/Symbol.h --- a/clang-tools-extra/clangd/index/Symbol.h +++ b/clang-tools-extra/clangd/index/Symbol.h @@ -63,6 +63,10 @@ /// candidate list. For example, "(X x, Y y) const" is a function signature. /// Only set when the symbol is indexed for completion. llvm::StringRef Signature; + /// Argument list in human-readable format, will be displayed to help + /// disambiguate between different specializations of a template. Empty for + /// non-specializations. Example: "" + llvm::StringRef TemplateSpecializationArgs; /// What to insert when completing this symbol, after the symbol name. /// This is in LSP snippet syntax (e.g. "({$0})" for a no-args function). /// (When snippets are disabled, the symbol name alone is used). @@ -143,6 +147,7 @@ template void visitStrings(Symbol &S, const Callback &CB) { CB(S.Name); CB(S.Scope); + CB(S.TemplateSpecializationArgs); CB(S.Signature); CB(S.CompletionSnippetSuffix); CB(S.Documentation); diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -561,6 +561,9 @@ formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion, /*CommentsFromHeaders=*/true)); if (!(S.Flags & Symbol::IndexedForCodeCompletion)) { + std::string TemplateSpecializationArgs = + printTemplateSpecializationArgs(ND); + S.TemplateSpecializationArgs = TemplateSpecializationArgs; if (Opts.StoreAllDocumentation) S.Documentation = Documentation; Symbols.insert(S); diff --git a/clang-tools-extra/clangd/index/YAMLSerialization.cpp b/clang-tools-extra/clangd/index/YAMLSerialization.cpp --- a/clang-tools-extra/clangd/index/YAMLSerialization.cpp +++ b/clang-tools-extra/clangd/index/YAMLSerialization.cpp @@ -193,6 +193,8 @@ IO.mapOptional("Origin", NSymbolOrigin->Origin); IO.mapOptional("Flags", NSymbolFlag->Flag); IO.mapOptional("Signature", Sym.Signature); + IO.mapOptional("TemplateSpecializationArgs", + Sym.TemplateSpecializationArgs); IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); IO.mapOptional("Documentation", Sym.Documentation); IO.mapOptional("ReturnType", Sym.ReturnType); diff --git a/clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp b/clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp --- a/clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp +++ b/clang-tools-extra/unittests/clangd/SymbolCollectorTests.cpp @@ -51,6 +51,9 @@ return (arg.Name + arg.CompletionSnippetSuffix).str() == S; } MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } +MATCHER_P(TemplateArgs, TemplArgs, "") { + return arg.TemplateSpecializationArgs == TemplArgs; +} MATCHER_P(DeclURI, P, "") { return StringRef(arg.CanonicalDeclaration.FileURI) == P; } @@ -413,6 +416,71 @@ ForCodeCompletion(false)))); } +TEST_F(SymbolCollectorTest, TemplateArgs) { + Annotations Header(R"( + template class $barclasstemp[[Bar]] {}; + template class Z, int Q> + struct [[Tmpl]] { T $xdecl[[x]] = 0; }; + + // template-template, non-type and type full spec + template <> struct $specdecl[[Tmpl]] {}; + + // template-template, non-type and type partial spec + template struct $partspecdecl[[Tmpl]] {}; + // instantiation + extern template struct Tmpl; + // instantiation + template struct Tmpl; + + template class $fooclasstemp[[Foo]] {}; + // parameter-packs full spec + template<> class $parampack[[Foo]], int, double> {}; + // parameter-packs partial spec + template class $parampackpartial[[Foo]] {}; + + template class $bazclasstemp[[Baz]] {}; + // non-type parameter-packs full spec + template<> class $parampacknontype[[Baz]]<3, 5, 8> {}; + // non-type parameter-packs partial spec + template class $parampacknontypepartial[[Baz]] {}; + + template