diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -108,6 +108,7 @@ return CompletionItemKind::Function; case SK::Variable: case SK::Parameter: + case SK::NonTypeTemplateParm: return CompletionItemKind::Variable; case SK::Field: return CompletionItemKind::Field; @@ -125,6 +126,9 @@ return CompletionItemKind::Property; case SK::Constructor: return CompletionItemKind::Constructor; + case SK::TemplateTypeParm: + case SK::TemplateTemplateParm: + return CompletionItemKind::TypeParameter; } llvm_unreachable("Unhandled clang::index::SymbolKind."); } diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -115,15 +115,6 @@ return Definition; } -void printParams(llvm::raw_ostream &OS, - const std::vector &Params) { - for (size_t I = 0, E = Params.size(); I != E; ++I) { - if (I) - OS << ", "; - OS << Params.at(I); - } -} - std::string printType(QualType QT, const PrintingPolicy &Policy) { // TypePrinter doesn't resolve decltypes, so resolve them here. // FIXME: This doesn't handle composite types that contain a decltype in them. @@ -133,6 +124,43 @@ return QT.getAsString(Policy); } +std::string printType(const TemplateTypeParmDecl *TTP) { + std::string Res = TTP->wasDeclaredWithTypename() ? "typename" : "class"; + if (TTP->isParameterPack()) + Res += "..."; + return Res; +} + +std::string printType(const NonTypeTemplateParmDecl *NTTP, + const PrintingPolicy &PP) { + std::string Res = printType(NTTP->getType(), PP); + if (NTTP->isParameterPack()) + Res += "..."; + return Res; +} + +std::string printType(const TemplateTemplateParmDecl *TTP, + const PrintingPolicy &PP) { + std::string Res; + llvm::raw_string_ostream OS(Res); + OS << "template <"; + llvm::StringRef Sep = ""; + for (const Decl *Param : *TTP->getTemplateParameters()) { + OS << Sep; + Sep = ", "; + if (const auto *TTP = dyn_cast(Param)) + OS << printType(TTP); + else if (const auto *NTTP = dyn_cast(Param)) + OS << printType(NTTP, PP); + else if (const auto *TTPD = dyn_cast(Param)) + OS << printType(TTPD, PP); + } + // FIXME: TemplateTemplateParameter doesn't store the info on whether this + // param was a "typename" or "class". + OS << "> class"; + return OS.str(); +} + std::vector fetchTemplateParameters(const TemplateParameterList *Params, const PrintingPolicy &PP) { @@ -142,38 +170,30 @@ for (const Decl *Param : *Params) { HoverInfo::Param P; if (const auto *TTP = dyn_cast(Param)) { - P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class"; - if (TTP->isParameterPack()) - *P.Type += "..."; + P.Type = printType(TTP); if (!TTP->getName().empty()) P.Name = TTP->getNameAsString(); + if (TTP->hasDefaultArgument()) P.Default = TTP->getDefaultArgument().getAsString(PP); } else if (const auto *NTTP = dyn_cast(Param)) { + P.Type = printType(NTTP, PP); + if (IdentifierInfo *II = NTTP->getIdentifier()) P.Name = II->getName().str(); - P.Type = printType(NTTP->getType(), PP); - if (NTTP->isParameterPack()) - *P.Type += "..."; - if (NTTP->hasDefaultArgument()) { P.Default.emplace(); llvm::raw_string_ostream Out(*P.Default); NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP); } } else if (const auto *TTPD = dyn_cast(Param)) { - P.Type.emplace(); - llvm::raw_string_ostream OS(*P.Type); - OS << "template <"; - printParams(OS, - fetchTemplateParameters(TTPD->getTemplateParameters(), PP)); - OS << "> class"; // FIXME: TemplateTemplateParameter doesn't store the - // info on whether this param was a "typename" or - // "class". + P.Type = printType(TTPD, PP); + if (!TTPD->getName().empty()) P.Name = TTPD->getNameAsString(); + if (TTPD->hasDefaultArgument()) { P.Default.emplace(); llvm::raw_string_ostream Out(*P.Default); @@ -385,6 +405,10 @@ fillFunctionTypeAndParams(HI, D, FD, Policy); else if (const auto *VD = dyn_cast(D)) HI.Type = printType(VD->getType(), Policy); + else if (const auto *TTP = dyn_cast(D)) + HI.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class"; + else if (const auto *TTP = dyn_cast(D)) + HI.Type = printType(TTP, Policy); // Fill in value with evaluated initializer if possible. if (const auto *Var = dyn_cast(D)) { diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -14,6 +14,7 @@ #include "Logger.h" #include "URI.h" #include "clang/Basic/LLVM.h" +#include "clang/Index/IndexSymbol.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -261,9 +262,13 @@ case index::SymbolKind::ConversionFunction: return SymbolKind::Function; case index::SymbolKind::Parameter: + case index::SymbolKind::NonTypeTemplateParm: return SymbolKind::Variable; case index::SymbolKind::Using: return SymbolKind::Namespace; + case index::SymbolKind::TemplateTemplateParm: + case index::SymbolKind::TemplateTypeParm: + return SymbolKind::TypeParameter; } llvm_unreachable("invalid symbol kind"); } diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -129,6 +129,8 @@ case index::SymbolKind::Extension: case index::SymbolKind::Union: case index::SymbolKind::TypeAlias: + case index::SymbolKind::TemplateTypeParm: + case index::SymbolKind::TemplateTemplateParm: return SymbolQualitySignals::Type; case index::SymbolKind::Function: case index::SymbolKind::ClassMethod: @@ -147,6 +149,7 @@ case index::SymbolKind::Field: case index::SymbolKind::EnumConstant: case index::SymbolKind::Parameter: + case index::SymbolKind::NonTypeTemplateParm: return SymbolQualitySignals::Variable; case index::SymbolKind::Using: case index::SymbolKind::Module: diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -573,6 +573,42 @@ // pattern. HI.Documentation = "comment from primary"; }}, + {// Template Type Parameter + R"cpp( + template void foo(); + )cpp", + [](HoverInfo &HI) { + HI.Name = "T"; + HI.Kind = index::SymbolKind::TemplateTypeParm; + HI.NamespaceScope = ""; + HI.Definition = "typename T = int"; + HI.LocalScope = "foo::"; + HI.Type = "typename"; + }}, + {// TemplateTemplate Type Parameter + R"cpp( + template class [[^T]]> void foo(); + )cpp", + [](HoverInfo &HI) { + HI.Name = "T"; + HI.Kind = index::SymbolKind::TemplateTemplateParm; + HI.NamespaceScope = ""; + HI.Definition = "template class T"; + HI.LocalScope = "foo::"; + HI.Type = "template class"; + }}, + {// NonType Template Parameter + R"cpp( + template void foo(); + )cpp", + [](HoverInfo &HI) { + HI.Name = "T"; + HI.Kind = index::SymbolKind::NonTypeTemplateParm; + HI.NamespaceScope = ""; + HI.Definition = "int T = 5"; + HI.LocalScope = "foo::"; + HI.Type = "int"; + }}, }; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Code); diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -54,6 +54,9 @@ Parameter, Using, + TemplateTypeParm, + TemplateTemplateParm, + NonTypeTemplateParm, }; enum class SymbolLanguage : uint8_t { diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -357,6 +357,15 @@ 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; // Other decls get the 'unknown' kind. default: break; @@ -517,6 +526,9 @@ 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"; } llvm_unreachable("invalid symbol kind"); } diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp --- a/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -1245,6 +1245,9 @@ case SymbolKind::Macro: case SymbolKind::ClassProperty: case SymbolKind::Using: + case SymbolKind::TemplateTypeParm: + case SymbolKind::TemplateTemplateParm: + case SymbolKind::NonTypeTemplateParm: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; diff --git a/clang/unittests/Index/IndexTests.cpp b/clang/unittests/Index/IndexTests.cpp --- a/clang/unittests/Index/IndexTests.cpp +++ b/clang/unittests/Index/IndexTests.cpp @@ -249,8 +249,13 @@ Index->Symbols.clear(); tooling::runToolOnCode(std::make_unique(Index, Opts), Code); EXPECT_THAT(Index->Symbols, - AllOf(Contains(QName("Foo::T")), Contains(QName("Foo::I")), - Contains(QName("Foo::C")), Contains(QName("Foo::NoRef")))); + AllOf(Contains(AllOf(QName("Foo::T"), + Kind(SymbolKind::TemplateTypeParm))), + Contains(AllOf(QName("Foo::I"), + Kind(SymbolKind::NonTypeTemplateParm))), + Contains(AllOf(QName("Foo::C"), + Kind(SymbolKind::TemplateTemplateParm))), + Contains(QName("Foo::NoRef")))); } TEST(IndexTest, UsingDecls) {