summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2021-12-29 04:16:47 +0100
committerSam McCall <sam.mccall@gmail.com>2022-01-03 16:28:16 +0100
commitcd45e8c7bc16dec2eeec9cc71eb3ba87d1bd6bab (patch)
tree3db9bfda04d2f78dc9bf0067bc2b4ebf56c9258a
parent3a33c0b1ce0db465c9d85c493674efc6c5005dbe (diff)
downloadllvm-cd45e8c7bc16dec2eeec9cc71eb3ba87d1bd6bab.tar.gz
[CodeCompletion] Signature help for template argument lists
Provide signature while typing template arguments: Foo< ^here > Here the parameters are e.g. "typename x", and the result type is e.g. "struct" (class template) or "int" (variable template) or "bool (std::string)" (function template). Multiple overloads are possible when a template name is used for several overloaded function templates. Fixes https://github.com/clangd/clangd/issues/299 Differential Revision: https://reviews.llvm.org/D116352
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.cpp2
-rw-r--r--clang-tools-extra/clangd/CodeComplete.cpp24
-rw-r--r--clang-tools-extra/clangd/test/initialize-params.test4
-rw-r--r--clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp19
-rw-r--r--clang/include/clang/Parse/Parser.h6
-rw-r--r--clang/include/clang/Sema/CodeCompleteConsumer.h21
-rw-r--r--clang/include/clang/Sema/Sema.h2
-rw-r--r--clang/lib/Parse/ParseExprCXX.cpp4
-rw-r--r--clang/lib/Parse/ParseTemplate.cpp40
-rw-r--r--clang/lib/Sema/CodeCompleteConsumer.cpp11
-rw-r--r--clang/lib/Sema/SemaCodeComplete.cpp138
-rw-r--r--clang/test/CodeCompletion/template-signature.cpp28
12 files changed, 266 insertions, 33 deletions
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 18539877ec97..774cdea218d0 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -555,7 +555,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
}},
{"signatureHelpProvider",
llvm::json::Object{
- {"triggerCharacters", {"(", ",", ")"}},
+ {"triggerCharacters", {"(", ",", ")", "<", ">"}},
}},
{"declarationProvider", true},
{"definitionProvider", true},
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 1c6b91e34828..bdfa1df19453 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -895,14 +895,12 @@ struct ScoredSignature {
// part of it.
int paramIndexForArg(const CodeCompleteConsumer::OverloadCandidate &Candidate,
int Arg) {
- int NumParams = 0;
+ int NumParams = Candidate.getNumParams();
if (const auto *F = Candidate.getFunction()) {
- NumParams = F->getNumParams();
if (F->isVariadic())
++NumParams;
} else if (auto *T = Candidate.getFunctionType()) {
if (auto *Proto = T->getAs<FunctionProtoType>()) {
- NumParams = Proto->getNumParams();
if (Proto->isVariadic())
++NumParams;
}
@@ -1016,6 +1014,9 @@ public:
return R.Quality.Kind != OC::CK_Function;
case OC::CK_FunctionTemplate:
return false;
+ case OC::CK_Template:
+ assert(false && "Never see templates and other overloads mixed");
+ return false;
}
llvm_unreachable("Unknown overload candidate type.");
}
@@ -1168,13 +1169,18 @@ public:
for (unsigned I = 0; I < NumCandidates; ++I) {
OverloadCandidate Candidate = Candidates[I];
- auto *Func = Candidate.getFunction();
- if (!Func || Func->getNumParams() <= CurrentArg)
- continue;
- auto *PVD = Func->getParamDecl(CurrentArg);
- if (!PVD)
+ NamedDecl *Param = nullptr;
+ if (auto *Func = Candidate.getFunction()) {
+ if (CurrentArg < Func->getNumParams())
+ Param = Func->getParamDecl(CurrentArg);
+ } else if (auto *Template = Candidate.getTemplate()) {
+ if (CurrentArg < Template->getTemplateParameters()->size())
+ Param = Template->getTemplateParameters()->getParam(CurrentArg);
+ }
+
+ if (!Param)
continue;
- auto *Ident = PVD->getIdentifier();
+ auto *Ident = Param->getIdentifier();
if (!Ident)
continue;
auto Name = Ident->getName();
diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test
index a79f1075118a..72823f3a0683 100644
--- a/clang-tools-extra/clangd/test/initialize-params.test
+++ b/clang-tools-extra/clangd/test/initialize-params.test
@@ -108,7 +108,9 @@
# CHECK-NEXT: "triggerCharacters": [
# CHECK-NEXT: "(",
# CHECK-NEXT: ",",
-# CHECK-NEXT: ")"
+# CHECK-NEXT: ")",
+# CHECK-NEXT: "<",
+# CHECK-NEXT: ">"
# CHECK-NEXT: ]
# CHECK-NEXT: },
# CHECK-NEXT: "textDocumentSync": {
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 2d4236d0763f..d32950fd6e13 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -3453,6 +3453,25 @@ TEST(SignatureHelp, DocFormat) {
}
}
+TEST(SignatureHelp, TemplateArguments) {
+ std::string Top = R"cpp(
+ template <typename T, int> bool foo(char);
+ template <int I, int> bool foo(float);
+ )cpp";
+
+ auto First = signatures(Top + "bool x = foo<^");
+ EXPECT_THAT(
+ First.signatures,
+ UnorderedElementsAre(Sig("foo<[[typename T]], [[int]]>() -> bool"),
+ Sig("foo<[[int I]], [[int]]>() -> bool")));
+ EXPECT_EQ(First.activeParameter, 0);
+
+ auto Second = signatures(Top + "bool x = foo<1, ^");
+ EXPECT_THAT(Second.signatures,
+ ElementsAre(Sig("foo<[[int I]], [[int]]>() -> bool")));
+ EXPECT_EQ(Second.activeParameter, 1);
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 741a484390b2..fd2221f03086 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3454,7 +3454,8 @@ private:
bool ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
SourceLocation &LAngleLoc,
TemplateArgList &TemplateArgs,
- SourceLocation &RAngleLoc);
+ SourceLocation &RAngleLoc,
+ TemplateTy NameHint = nullptr);
bool AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
CXXScopeSpec &SS,
@@ -3464,7 +3465,8 @@ private:
bool TypeConstraint = false);
void AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
bool IsClassName = false);
- bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
+ bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
+ TemplateTy Template, SourceLocation OpenLoc);
ParsedTemplateArgument ParseTemplateTemplateArgument();
ParsedTemplateArgument ParseTemplateArgument();
Decl *ParseExplicitInstantiation(DeclaratorContext Context,
diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h
index 6b37e3c50dba..7a369dfd6a43 100644
--- a/clang/include/clang/Sema/CodeCompleteConsumer.h
+++ b/clang/include/clang/Sema/CodeCompleteConsumer.h
@@ -1009,12 +1009,15 @@ public:
/// The candidate is a function declaration.
CK_Function,
- /// The candidate is a function template.
+ /// The candidate is a function template, arguments are being completed.
CK_FunctionTemplate,
/// The "candidate" is actually a variable, expression, or block
/// for which we only have a function prototype.
- CK_FunctionType
+ CK_FunctionType,
+
+ /// The candidate is a template, template arguments are being completed.
+ CK_Template,
};
private:
@@ -1033,6 +1036,10 @@ public:
/// The function type that describes the entity being called,
/// when Kind == CK_FunctionType.
const FunctionType *Type;
+
+ /// The template overload candidate, available when
+ /// Kind == CK_Template.
+ const TemplateDecl *Template;
};
public:
@@ -1045,6 +1052,9 @@ public:
OverloadCandidate(const FunctionType *Type)
: Kind(CK_FunctionType), Type(Type) {}
+ OverloadCandidate(const TemplateDecl *Template)
+ : Kind(CK_Template), Template(Template) {}
+
/// Determine the kind of overload candidate.
CandidateKind getKind() const { return Kind; }
@@ -1062,6 +1072,13 @@ public:
/// function is stored.
const FunctionType *getFunctionType() const;
+ const TemplateDecl *getTemplate() const {
+ assert(getKind() == CK_Template && "Not a template");
+ return Template;
+ }
+
+ unsigned getNumParams() const;
+
/// Create a new code-completion string that describes the function
/// signature of this overload candidate.
CodeCompletionString *CreateSignatureString(unsigned CurrentArg,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f97a785c7426..bb13d9527175 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12549,6 +12549,8 @@ public:
ArrayRef<Expr *> ArgExprs,
IdentifierInfo *II,
SourceLocation OpenParLoc);
+ QualType ProduceTemplateArgumentSignatureHelp(
+ TemplateTy, ArrayRef<ParsedTemplateArgument>, SourceLocation LAngleLoc);
void CodeCompleteInitializer(Scope *S, Decl *D);
/// Trigger code completion for a record of \p BaseType. \p InitExprs are
/// expressions in the initializer list seen so far and \p D is the current
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 76c510ddd36c..9cdc16f8ce8d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -2454,8 +2454,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
- if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs,
- RAngleLoc))
+ if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs, RAngleLoc,
+ Template))
return true;
// If this is a non-template, we already issued a diagnostic.
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 45af61a3926a..204b53441ab4 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1222,7 +1222,6 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
return false;
}
-
/// Parses a template-id that after the template name has
/// already been parsed.
///
@@ -1234,11 +1233,13 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
/// token that forms the template-id. Otherwise, we will leave the
/// last token in the stream (e.g., so that it can be replaced with an
/// annotation token).
-bool
-Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
- SourceLocation &LAngleLoc,
- TemplateArgList &TemplateArgs,
- SourceLocation &RAngleLoc) {
+///
+/// \param NameHint is not required, and merely affects code completion.
+bool Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
+ SourceLocation &LAngleLoc,
+ TemplateArgList &TemplateArgs,
+ SourceLocation &RAngleLoc,
+ TemplateTy Template) {
assert(Tok.is(tok::less) && "Must have already parsed the template-name");
// Consume the '<'.
@@ -1251,7 +1252,7 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
if (!Tok.isOneOf(tok::greater, tok::greatergreater,
tok::greatergreatergreater, tok::greaterequal,
tok::greatergreaterequal))
- Invalid = ParseTemplateArgumentList(TemplateArgs);
+ Invalid = ParseTemplateArgumentList(TemplateArgs, Template, LAngleLoc);
if (Invalid) {
// Try to find the closing '>'.
@@ -1332,8 +1333,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
TemplateArgList TemplateArgs;
bool ArgsInvalid = false;
if (!TypeConstraint || Tok.is(tok::less)) {
- ArgsInvalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
- TemplateArgs, RAngleLoc);
+ ArgsInvalid = ParseTemplateIdAfterTemplateName(
+ false, LAngleLoc, TemplateArgs, RAngleLoc, Template);
// If we couldn't recover from invalid arguments, don't form an annotation
// token -- we don't know how much to annotate.
// FIXME: This can lead to duplicate diagnostics if we retry parsing this
@@ -1585,19 +1586,34 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
/// template-argument-list: [C++ 14.2]
/// template-argument
/// template-argument-list ',' template-argument
-bool
-Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) {
+///
+/// \param Template is only used for code completion, and may be null.
+bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
+ TemplateTy Template,
+ SourceLocation OpenLoc) {
ColonProtectionRAIIObject ColonProtection(*this, false);
+ auto RunSignatureHelp = [&] {
+ if (!Template)
+ return QualType();
+ CalledSignatureHelp = true;
+ return Actions.ProduceTemplateArgumentSignatureHelp(Template, TemplateArgs,
+ OpenLoc);
+ };
+
do {
+ PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp);
ParsedTemplateArgument Arg = ParseTemplateArgument();
SourceLocation EllipsisLoc;
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc);
- if (Arg.isInvalid())
+ if (Arg.isInvalid()) {
+ if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
+ RunSignatureHelp();
return true;
+ }
// Save this template argument.
TemplateArgs.push_back(Arg);
diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp
index 0a2ca54e244a..f0968ed0e503 100644
--- a/clang/lib/Sema/CodeCompleteConsumer.cpp
+++ b/clang/lib/Sema/CodeCompleteConsumer.cpp
@@ -506,11 +506,22 @@ CodeCompleteConsumer::OverloadCandidate::getFunctionType() const {
case CK_FunctionType:
return Type;
+
+ case CK_Template:
+ return nullptr;
}
llvm_unreachable("Invalid CandidateKind!");
}
+unsigned CodeCompleteConsumer::OverloadCandidate::getNumParams() const {
+ if (Kind == CK_Template)
+ return Template->getTemplateParameters()->size();
+ if (const auto *FPT = dyn_cast_or_null<FunctionProtoType>(getFunctionType()))
+ return FPT->getNumParams();
+ return 0;
+}
+
//===----------------------------------------------------------------------===//
// Code completion consumer implementation
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index be492b5f3607..e81faf6d2a93 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -36,6 +36,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ParsedAttr.h"
+#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
@@ -3757,6 +3758,78 @@ static void AddOverloadParameterChunks(ASTContext &Context,
}
}
+static std::string
+formatTemplateParameterPlaceholder(const NamedDecl *Param, bool &Optional,
+ const PrintingPolicy &Policy) {
+ if (const auto *Type = dyn_cast<TemplateTypeParmDecl>(Param)) {
+ Optional = Type->hasDefaultArgument();
+ } else if (const auto *NonType = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ Optional = NonType->hasDefaultArgument();
+ } else if (const auto *Template = dyn_cast<TemplateTemplateParmDecl>(Param)) {
+ Optional = Template->hasDefaultArgument();
+ }
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ Param->print(OS, Policy);
+ return Result;
+}
+
+static std::string templateResultType(const TemplateDecl *TD,
+ const PrintingPolicy &Policy) {
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD))
+ return CTD->getTemplatedDecl()->getKindName().str();
+ if (const auto *VTD = dyn_cast<VarTemplateDecl>(TD))
+ return VTD->getTemplatedDecl()->getType().getAsString(Policy);
+ if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(TD))
+ return FTD->getTemplatedDecl()->getReturnType().getAsString(Policy);
+ if (isa<TypeAliasTemplateDecl>(TD))
+ return "type";
+ if (isa<TemplateTemplateParmDecl>(TD))
+ return "class";
+ if (isa<ConceptDecl>(TD))
+ return "concept";
+ return "";
+}
+
+static CodeCompletionString *createTemplateSignatureString(
+ const TemplateDecl *TD, CodeCompletionBuilder &Builder, unsigned CurrentArg,
+ const PrintingPolicy &Policy) {
+ llvm::ArrayRef<NamedDecl *> Params = TD->getTemplateParameters()->asArray();
+ CodeCompletionBuilder OptionalBuilder(Builder.getAllocator(),
+ Builder.getCodeCompletionTUInfo());
+ std::string ResultType = templateResultType(TD, Policy);
+ if (!ResultType.empty())
+ Builder.AddResultTypeChunk(Builder.getAllocator().CopyString(ResultType));
+ Builder.AddTextChunk(
+ Builder.getAllocator().CopyString(TD->getNameAsString()));
+ Builder.AddChunk(CodeCompletionString::CK_LeftAngle);
+ // Initially we're writing into the main string. Once we see an optional arg
+ // (with default), we're writing into the nested optional chunk.
+ CodeCompletionBuilder *Current = &Builder;
+ for (unsigned I = 0; I < Params.size(); ++I) {
+ bool Optional = false;
+ std::string Placeholder =
+ formatTemplateParameterPlaceholder(Params[I], Optional, Policy);
+ if (Optional)
+ Current = &OptionalBuilder;
+ if (I > 0)
+ Current->AddChunk(CodeCompletionString::CK_Comma);
+ Current->AddChunk(I == CurrentArg
+ ? CodeCompletionString::CK_CurrentParameter
+ : CodeCompletionString::CK_Placeholder,
+ Current->getAllocator().CopyString(Placeholder));
+ }
+ // Add the optional chunk to the main string if we ever used it.
+ if (Current == &OptionalBuilder)
+ Builder.AddOptionalChunk(OptionalBuilder.TakeString());
+ Builder.AddChunk(CodeCompletionString::CK_RightAngle);
+ // For function templates, ResultType was the function's return type.
+ // Give some clue this is a function. (Don't show the possibly-bulky params).
+ if (isa<FunctionTemplateDecl>(TD))
+ Builder.AddInformativeChunk("()");
+ return Builder.TakeString();
+}
+
CodeCompletionString *
CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
unsigned CurrentArg, Sema &S, CodeCompletionAllocator &Allocator,
@@ -3770,6 +3843,11 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
// FIXME: Set priority, availability appropriately.
CodeCompletionBuilder Result(Allocator, CCTUInfo, 1,
CXAvailability_Available);
+
+ if (getKind() == CK_Template)
+ return createTemplateSignatureString(getTemplate(), Result, CurrentArg,
+ Policy);
+
FunctionDecl *FDecl = getFunction();
const FunctionProtoType *Proto =
dyn_cast<FunctionProtoType>(getFunctionType());
@@ -5843,6 +5921,7 @@ static QualType getParamType(Sema &SemaRef,
// overload candidates.
QualType ParamType;
for (auto &Candidate : Candidates) {
+ // FIXME: handle non-type-template-parameters by merging with D116326
if (const auto *FType = Candidate.getFunctionType())
if (const auto *Proto = dyn_cast<FunctionProtoType>(FType))
if (N < Proto->getNumParams()) {
@@ -5860,8 +5939,7 @@ static QualType getParamType(Sema &SemaRef,
}
static QualType
-ProduceSignatureHelp(Sema &SemaRef, Scope *S,
- MutableArrayRef<ResultCandidate> Candidates,
+ProduceSignatureHelp(Sema &SemaRef, MutableArrayRef<ResultCandidate> Candidates,
unsigned CurrentArg, SourceLocation OpenParLoc) {
if (Candidates.empty())
return QualType();
@@ -5970,7 +6048,7 @@ QualType Sema::ProduceCallSignatureHelp(Scope *S, Expr *Fn,
}
mergeCandidatesWithResults(*this, Results, CandidateSet, Loc, Args.size());
QualType ParamType =
- ProduceSignatureHelp(*this, S, Results, Args.size(), OpenParLoc);
+ ProduceSignatureHelp(*this, Results, Args.size(), OpenParLoc);
return !CandidateSet.empty() ? ParamType : QualType();
}
@@ -6010,7 +6088,7 @@ QualType Sema::ProduceConstructorSignatureHelp(Scope *S, QualType Type,
SmallVector<ResultCandidate, 8> Results;
mergeCandidatesWithResults(*this, Results, CandidateSet, Loc, Args.size());
- return ProduceSignatureHelp(*this, S, Results, Args.size(), OpenParLoc);
+ return ProduceSignatureHelp(*this, Results, Args.size(), OpenParLoc);
}
QualType Sema::ProduceCtorInitMemberSignatureHelp(
@@ -6032,6 +6110,58 @@ QualType Sema::ProduceCtorInitMemberSignatureHelp(
return QualType();
}
+static bool argMatchesTemplateParams(const ParsedTemplateArgument &Arg,
+ unsigned Index,
+ const TemplateParameterList &Params) {
+ const NamedDecl *Param;
+ if (Index < Params.size())
+ Param = Params.getParam(Index);
+ else if (Params.hasParameterPack())
+ Param = Params.asArray().back();
+ else
+ return false; // too many args
+
+ switch (Arg.getKind()) {
+ case ParsedTemplateArgument::Type:
+ return llvm::isa<TemplateTypeParmDecl>(Param); // constraints not checked
+ case ParsedTemplateArgument::NonType:
+ return llvm::isa<NonTypeTemplateParmDecl>(Param); // type not checked
+ case ParsedTemplateArgument::Template:
+ return llvm::isa<TemplateTemplateParmDecl>(Param); // signature not checked
+ }
+}
+
+QualType Sema::ProduceTemplateArgumentSignatureHelp(
+ TemplateTy ParsedTemplate, ArrayRef<ParsedTemplateArgument> Args,
+ SourceLocation LAngleLoc) {
+ if (!CodeCompleter || !ParsedTemplate)
+ return QualType();
+
+ SmallVector<ResultCandidate, 8> Results;
+ auto Consider = [&](const TemplateDecl *TD) {
+ // Only add if the existing args are compatible with the template.
+ bool Matches = true;
+ for (unsigned I = 0; I < Args.size(); ++I) {
+ if (!argMatchesTemplateParams(Args[I], I, *TD->getTemplateParameters())) {
+ Matches = false;
+ break;
+ }
+ }
+ if (Matches)
+ Results.emplace_back(TD);
+ };
+
+ TemplateName Template = ParsedTemplate.get();
+ if (const auto *TD = Template.getAsTemplateDecl()) {
+ Consider(TD);
+ } else if (const auto *OTS = Template.getAsOverloadedTemplate()) {
+ for (const NamedDecl *ND : *OTS)
+ if (const auto *TD = llvm::dyn_cast<TemplateDecl>(ND))
+ Consider(TD);
+ }
+ return ProduceSignatureHelp(*this, Results, Args.size(), LAngleLoc);
+}
+
static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
for (unsigned I = 0; I < Desig.getNumDesignators(); ++I) {
if (BaseType.isNull())
diff --git a/clang/test/CodeCompletion/template-signature.cpp b/clang/test/CodeCompletion/template-signature.cpp
new file mode 100644
index 000000000000..4425faf24912
--- /dev/null
+++ b/clang/test/CodeCompletion/template-signature.cpp
@@ -0,0 +1,28 @@
+template <int, char y> float overloaded(int);
+template <class, int x> bool overloaded(char);
+
+auto m = overloaded<1, 2>(0);
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:4:21 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: OPENING_PAREN_LOC: {{.*}}4:20
+// CHECK-CC1-DAG: OVERLOAD: [#float#]overloaded<<#int#>, char y>[#()#]
+// CHECK-CC1-DAG: OVERLOAD: [#bool#]overloaded<<#class#>, int x>[#()#]
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:4:24 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2-NOT: OVERLOAD: {{.*}}int x
+// CHECK-CC2: OVERLOAD: [#float#]overloaded<int, <#char y#>>[#()#]
+
+template <class T, T... args> int n = 0;
+int val = n<int, 1, 2, 3>;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:14:18 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: OVERLOAD: [#int#]n<class T, <#T ...args#>>
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:14:24 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s
+// CHECK-CC4: OVERLOAD: [#int#]n<class T, T ...args>
+
+template <typename> struct Vector {};
+template <typename Element, template <typename E> class Container = Vector>
+struct Collection { Container<Element> container; };
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:22:31 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s
+// CHECK-CC5: OVERLOAD: [#class#]Container<<#typename E#>>
+Collection<int, Vector> collection;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:25:12 %s -o - | FileCheck -check-prefix=CHECK-CC6 %s
+// CHECK-CC6: OVERLOAD: [#struct#]Collection<<#typename Element#>>
+