[clang][Index] Introduce a TemplateParm SymbolKind

Summary:
Currently template parameters has symbolkind `Unknown`. This patch
introduces a new kind `TemplateParm` for templatetemplate, templatetype and
nontypetemplate parameters.

Also adds tests in clangd hover feature.

Reviewers: sammccall

Subscribers: kristof.beyls, ilya-biryukov, jkorous, arphaman, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D73696
This commit is contained in:
Kadir Cetinkaya 2020-01-30 14:07:42 +01:00
parent c45fb35b5e
commit 84240e0db8
No known key found for this signature in database
GPG Key ID: E39E36B8D2057ED6
9 changed files with 121 additions and 26 deletions

View File

@ -108,6 +108,7 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
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 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
return CompletionItemKind::Property;
case SK::Constructor:
return CompletionItemKind::Constructor;
case SK::TemplateTypeParm:
case SK::TemplateTemplateParm:
return CompletionItemKind::TypeParameter;
}
llvm_unreachable("Unhandled clang::index::SymbolKind.");
}

View File

@ -115,15 +115,6 @@ std::string printDefinition(const Decl *D) {
return Definition;
}
void printParams(llvm::raw_ostream &OS,
const std::vector<HoverInfo::Param> &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 @@ std::string printType(QualType QT, const PrintingPolicy &Policy) {
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<TemplateTypeParmDecl>(Param))
OS << printType(TTP);
else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))
OS << printType(NTTP, PP);
else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(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<HoverInfo::Param>
fetchTemplateParameters(const TemplateParameterList *Params,
const PrintingPolicy &PP) {
@ -142,38 +170,30 @@ fetchTemplateParameters(const TemplateParameterList *Params,
for (const Decl *Param : *Params) {
HoverInfo::Param P;
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(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<NonTypeTemplateParmDecl>(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<TemplateTemplateParmDecl>(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 @@ HoverInfo getHoverContents(const NamedDecl *D, const SymbolIndex *Index) {
fillFunctionTypeAndParams(HI, D, FD, Policy);
else if (const auto *VD = dyn_cast<ValueDecl>(D))
HI.Type = printType(VD->getType(), Policy);
else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D))
HI.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class";
else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D))
HI.Type = printType(TTP, Policy);
// Fill in value with evaluated initializer if possible.
if (const auto *Var = dyn_cast<VarDecl>(D)) {

View File

@ -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 @@ SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
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");
}

View File

@ -129,6 +129,8 @@ categorize(const index::SymbolInfo &D) {
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 @@ categorize(const index::SymbolInfo &D) {
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:

View File

@ -573,6 +573,42 @@ class Foo {})cpp";
// pattern.
HI.Documentation = "comment from primary";
}},
{// Template Type Parameter
R"cpp(
template <typename [[^T]] = int> 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 <template<typename> class [[^T]]> void foo();
)cpp",
[](HoverInfo &HI) {
HI.Name = "T";
HI.Kind = index::SymbolKind::TemplateTemplateParm;
HI.NamespaceScope = "";
HI.Definition = "template <typename> class T";
HI.LocalScope = "foo::";
HI.Type = "template <typename> class";
}},
{// NonType Template Parameter
R"cpp(
template <int [[^T]] = 5> 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);

View File

@ -54,6 +54,9 @@ enum class SymbolKind : uint8_t {
Parameter,
Using,
TemplateTypeParm,
TemplateTemplateParm,
NonTypeTemplateParm,
};
enum class SymbolLanguage : uint8_t {

View File

@ -357,6 +357,15 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
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 @@ StringRef index::getSymbolKindString(SymbolKind K) {
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");
}

View File

@ -1245,6 +1245,9 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage
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;

View File

@ -249,8 +249,13 @@ TEST(IndexTest, IndexTypeParmDecls) {
Index->Symbols.clear();
tooling::runToolOnCode(std::make_unique<IndexAction>(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) {