forked from OSchip/llvm-project
[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
This commit is contained in:
parent
3a33c0b1ce
commit
cd45e8c7bc
|
@ -555,7 +555,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
|
|||
}},
|
||||
{"signatureHelpProvider",
|
||||
llvm::json::Object{
|
||||
{"triggerCharacters", {"(", ",", ")"}},
|
||||
{"triggerCharacters", {"(", ",", ")", "<", ">"}},
|
||||
}},
|
||||
{"declarationProvider", true},
|
||||
{"definitionProvider", true},
|
||||
|
|
|
@ -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)
|
||||
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 *PVD = Func->getParamDecl(CurrentArg);
|
||||
if (!PVD)
|
||||
continue;
|
||||
auto *Ident = PVD->getIdentifier();
|
||||
auto *Ident = Param->getIdentifier();
|
||||
if (!Ident)
|
||||
continue;
|
||||
auto Name = Ident->getName();
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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#>>
|
||||
|
Loading…
Reference in New Issue