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 @@ -942,6 +942,7 @@ for (unsigned I = 0; I < NumCandidates; ++I) { OverloadCandidate Candidate = Candidates[I]; + // We want to avoid showing instantiated signatures, because they may be // long in some cases (e.g. when 'T' is substituted with 'std::string', we // would get 'std::basic_string'). @@ -1007,10 +1008,12 @@ auto KindPriority = [&](OC::CandidateKind K) { switch (K) { case OC::CK_Aggregate: - return 1; + return 0; case OC::CK_Function: - return 2; + return 1; case OC::CK_FunctionType: + return 2; + case OC::CK_FunctionProtoTypeLoc: return 3; case OC::CK_FunctionTemplate: return 4; diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -1265,6 +1265,23 @@ EXPECT_EQ(0, Results.activeParameter); } +TEST(SignatureHelpTest, FunctionPointers) { + auto FunctionPointerResults = signatures(R"cpp( + void (*foo)(int x, int y); + int main() { foo(^); } + )cpp"); + EXPECT_THAT(FunctionPointerResults.signatures, + UnorderedElementsAre(sig("([[int x]], [[int y]]) -> void"))); + + auto FunctionPointerTypedefResults = signatures(R"cpp( + typedef void (*fn)(int x, int y); + fn foo; + int main() { foo(^); } + )cpp"); + EXPECT_THAT(FunctionPointerTypedefResults.signatures, + UnorderedElementsAre(sig("([[int x]], [[int y]]) -> void"))); +} + TEST(SignatureHelpTest, Constructors) { std::string Top = R"cpp( struct S { diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h --- a/clang/include/clang/Sema/CodeCompleteConsumer.h +++ b/clang/include/clang/Sema/CodeCompleteConsumer.h @@ -1016,6 +1016,8 @@ /// for which we only have a function prototype. CK_FunctionType, + CK_FunctionProtoTypeLoc, + /// The candidate is a template, template arguments are being completed. CK_Template, @@ -1040,6 +1042,8 @@ /// when Kind == CK_FunctionType. const FunctionType *Type; + FunctionProtoTypeLoc ProtoTypeLoc; + /// The template overload candidate, available when /// Kind == CK_Template. const TemplateDecl *Template; @@ -1065,6 +1069,11 @@ assert(Type != nullptr); } + OverloadCandidate(FunctionProtoTypeLoc Prototype) + : Kind(CK_FunctionProtoTypeLoc), ProtoTypeLoc(Prototype) { + assert(!Prototype.isNull()); + } + OverloadCandidate(const RecordDecl *Aggregate) : Kind(CK_Aggregate), AggregateType(Aggregate) { assert(Aggregate != nullptr); @@ -1090,6 +1099,9 @@ /// function is stored. const FunctionType *getFunctionType() const; + /// Retrieve the function ProtoTypeLoc candidate. + const FunctionProtoTypeLoc getFunctionProtoTypeLoc() const; + const TemplateDecl *getTemplate() const { assert(getKind() == CK_Template && "Not a template"); return Template; diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp --- a/clang/lib/Sema/CodeCompleteConsumer.cpp +++ b/clang/lib/Sema/CodeCompleteConsumer.cpp @@ -506,7 +506,8 @@ case CK_FunctionType: return Type; - + case CK_FunctionProtoTypeLoc: + return ProtoTypeLoc.getTypePtr(); case CK_Template: case CK_Aggregate: return nullptr; @@ -515,6 +516,12 @@ llvm_unreachable("Invalid CandidateKind!"); } + const FunctionProtoTypeLoc CodeCompleteConsumer::OverloadCandidate::getFunctionProtoTypeLoc() const { + if (Kind == CK_FunctionProtoTypeLoc) + return ProtoTypeLoc; + return FunctionProtoTypeLoc(); + } + unsigned CodeCompleteConsumer::OverloadCandidate::getNumParams() const { if (Kind == CK_Template) return Template->getTemplateParameters()->size(); @@ -530,6 +537,9 @@ if (const auto *FT = getFunctionType()) if (const auto *FPT = dyn_cast(FT)) return FPT->getNumParams(); + if (!getFunctionProtoTypeLoc().isNull()) { + return ProtoTypeLoc.getNumParams(); + } return 0; } @@ -589,6 +599,12 @@ if (N < FD->param_size()) return FD->getParamDecl(N); } + else if (!getFunctionProtoTypeLoc().isNull()) { + if (N < ProtoTypeLoc.getNumParams()) { + return ProtoTypeLoc.getParam(N); + } + } + return nullptr; } diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -3722,6 +3722,7 @@ const PrintingPolicy &Policy, const FunctionDecl *Function, const FunctionProtoType *Prototype, + FunctionProtoTypeLoc PrototypeLoc, CodeCompletionBuilder &Result, unsigned CurrentArg, unsigned Start = 0, bool InOptional = false) { @@ -3743,7 +3744,7 @@ if (!FirstParameter) Opt.AddChunk(CodeCompletionString::CK_Comma); // Optional sections are nested. - AddOverloadParameterChunks(Context, Policy, Function, Prototype, Opt, + AddOverloadParameterChunks(Context, Policy, Function, Prototype, PrototypeLoc, Opt, CurrentArg, P, /*InOptional=*/true); Result.AddOptionalChunk(Opt.TakeString()); return; @@ -3764,6 +3765,14 @@ if (Param->hasDefaultArg()) Placeholder += GetDefaultValueString(Param, Context.getSourceManager(), Context.getLangOpts()); + } else if (!PrototypeLoc.isNull()) { + if (P < PrototypeLoc.getNumParams()) { + const ParmVarDecl *Param = PrototypeLoc.getParam(P); + Placeholder = FormatFunctionParameter(Policy, Param); + if (Param->hasDefaultArg()) + Placeholder += GetDefaultValueString(Param, Context.getSourceManager(), + Context.getLangOpts()); + } } else { Placeholder = Prototype->getParamType(P).getAsString(Policy); } @@ -3912,7 +3921,7 @@ if (getKind() == CK_Aggregate) AddOverloadAggregateChunks(getAggregate(), Policy, Result, CurrentArg); else - AddOverloadParameterChunks(S.getASTContext(), Policy, FDecl, Proto, Result, + AddOverloadParameterChunks(S.getASTContext(), Policy, FDecl, Proto, getFunctionProtoTypeLoc(), Result, CurrentArg); Result.AddChunk(Braced ? CodeCompletionString::CK_RightBrace : CodeCompletionString::CK_RightParen); @@ -5991,6 +6000,41 @@ return getParamType(SemaRef, Candidates, CurrentArg); } +static FunctionProtoTypeLoc GetPrototypeLoc(Expr *Fn) { + if (const auto *T = Fn->getType().getTypePtr()->getAs()) { + const auto *D = T->getDecl(); + + TypeLoc TypedefTarget = D->getTypeSourceInfo()->getTypeLoc(); + if (auto P = TypedefTarget.getAs()) { + TypedefTarget = P.getPointeeLoc(); + } + if (auto P = TypedefTarget.getAs()) { + TypedefTarget = P.getInnerLoc(); + } + if (auto F = TypedefTarget.getAs()) { + return F; + } + } + else if (const auto *DR = dyn_cast(Fn)) { + const auto *D = DR->getDecl(); + if (const auto *const VD = dyn_cast(D)) { + TypeLoc Target = VD->getTypeSourceInfo()->getTypeLoc(); + + if (auto P = Target.getAs()) { + Target = P.getPointeeLoc(); + } + if (auto P = Target.getAs()) { + Target = P.getInnerLoc(); + } + if (auto F = Target.getAs()) { + return F; + } + } + } + + return FunctionProtoTypeLoc(); +} + QualType Sema::ProduceCallSignatureHelp(Expr *Fn, ArrayRef Args, SourceLocation OpenParLoc) { Fn = unwrapParenList(Fn); @@ -6072,6 +6116,8 @@ } else { // Lastly we check whether expression's type is function pointer or // function. + + FunctionProtoTypeLoc P = GetPrototypeLoc(NakedFn); QualType T = NakedFn->getType(); if (!T->getPointeeType().isNull()) T = T->getPointeeType(); @@ -6080,8 +6126,13 @@ if (!TooManyArguments(FP->getNumParams(), ArgsWithoutDependentTypes.size(), /*PartialOverloading=*/true) || - FP->isVariadic()) - Results.push_back(ResultCandidate(FP)); + FP->isVariadic()) { + if (!P.isNull()) { + Results.push_back(ResultCandidate(P)); + } else { + Results.push_back(ResultCandidate(FP)); + } + } } else if (auto FT = T->getAs()) // No prototype and declaration, it may be a K & R style function. Results.push_back(ResultCandidate(FT));