diff options
author | Sam McCall <sam.mccall@gmail.com> | 2021-12-29 04:16:47 +0100 |
---|---|---|
committer | Sam McCall <sam.mccall@gmail.com> | 2022-01-03 16:28:16 +0100 |
commit | cd45e8c7bc16dec2eeec9cc71eb3ba87d1bd6bab (patch) | |
tree | 3db9bfda04d2f78dc9bf0067bc2b4ebf56c9258a | |
parent | 3a33c0b1ce0db465c9d85c493674efc6c5005dbe (diff) | |
download | llvm-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.cpp | 2 | ||||
-rw-r--r-- | clang-tools-extra/clangd/CodeComplete.cpp | 24 | ||||
-rw-r--r-- | clang-tools-extra/clangd/test/initialize-params.test | 4 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp | 19 | ||||
-rw-r--r-- | clang/include/clang/Parse/Parser.h | 6 | ||||
-rw-r--r-- | clang/include/clang/Sema/CodeCompleteConsumer.h | 21 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTemplate.cpp | 40 | ||||
-rw-r--r-- | clang/lib/Sema/CodeCompleteConsumer.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaCodeComplete.cpp | 138 | ||||
-rw-r--r-- | clang/test/CodeCompletion/template-signature.cpp | 28 |
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#>> + |