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 @@ -1166,46 +1166,75 @@ } TEST(SignatureHelpTest, OpeningParen) { - llvm::StringLiteral Tests[] = {// Recursive function call. - R"cpp( - int foo(int a, int b, int c); - int main() { - foo(foo $p^( foo(10, 10, 10), ^ ))); - })cpp", - // Functional type cast. - R"cpp( - struct Foo { - Foo(int a, int b, int c); - }; - int main() { - Foo $p^( 10, ^ ); - })cpp", - // New expression. - R"cpp( - struct Foo { - Foo(int a, int b, int c); - }; - int main() { - new Foo $p^( 10, ^ ); - })cpp", - // Macro expansion. - R"cpp( - int foo(int a, int b, int c); - #define FOO foo( - - int main() { - // Macro expansions. - $p^FOO 10, ^ ); - })cpp", - // Macro arguments. - R"cpp( - int foo(int a, int b, int c); - int main() { - #define ID(X) X - // FIXME: figure out why ID(foo (foo(10), )) doesn't work when preserving - // the recovery expression. - ID(foo $p^( 10, ^ )) - })cpp"}; + llvm::StringLiteral Tests[] = { + // Recursive function call. + R"cpp( + int foo(int a, int b, int c); + int main() { + foo(foo $p^( foo(10, 10, 10), ^ ))); + })cpp", + // Functional type cast. + R"cpp( + struct Foo { + Foo(int a, int b, int c); + }; + int main() { + Foo $p^( 10, ^ ); + })cpp", + // New expression. + R"cpp( + struct Foo { + Foo(int a, int b, int c); + }; + int main() { + new Foo $p^( 10, ^ ); + })cpp", + // Macro expansion. + R"cpp( + int foo(int a, int b, int c); + #define FOO foo( + + int main() { + // Macro expansions. + $p^FOO 10, ^ ); + })cpp", + // Macro arguments. + R"cpp( + int foo(int a, int b, int c); + int main() { + #define ID(X) X + // FIXME: figure out why ID(foo (foo(10), )) doesn't work when preserving + // the recovery expression. + ID(foo $p^( 10, ^ )) + })cpp", + // Dependent args. + R"cpp( + int foo(int a, int b); + template void bar(T t) { + foo$p^(t, ^t); + })cpp", + // Dependent args on templated func. + R"cpp( + template + int foo(T, T); + template void bar(T t) { + foo$p^(t, ^t); + })cpp", + // Dependent args on member. + R"cpp( + struct Foo { int foo(int, int); }; + template void bar(T t) { + Foo f; + f.foo$p^(t, ^t); + })cpp", + // Dependent args on templated member. + R"cpp( + struct Foo { template int foo(T, T); }; + template void bar(T t) { + Foo f; + f.foo$p^(t, ^t); + })cpp", + }; for (auto Test : Tests) { Annotations Code(Test); 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 @@ -5562,22 +5562,43 @@ // FIXME: Provide support for variadic template functions. // Ignore type-dependent call expressions entirely. - if (!Fn || Fn->isTypeDependent() || anyNullArguments(Args) || - Expr::hasAnyTypeDependentArguments(Args)) { + if (!Fn || Fn->isTypeDependent() || anyNullArguments(Args)) return QualType(); + + SmallVector Results; + + Expr *NakedFn = Fn->IgnoreParenCasts(); + // In presence of dependent args we surface all possible signatures, without + // performing any semantic checks on availability. That's to improve user + // experience, it is better to see all overloads rather than none. + if (Expr::hasAnyTypeDependentArguments(Args)) { + auto AddIfArgCountChecks = [&Results, &Args](FunctionDecl *FD) { + if (FD->getNumParams() < Args.size()) + return; + Results.push_back(ResultCandidate(FD)); + }; + if (auto *ULE = dyn_cast(NakedFn)) { + for (auto *D : ULE->decls()) + AddIfArgCountChecks(D->getAsFunction()); + } else if (auto *UME = dyn_cast(NakedFn)) { + for (auto *D : UME->decls()) + AddIfArgCountChecks(D->getAsFunction()); + } else if (auto *ME = dyn_cast(NakedFn)) { + if (auto *FD = ME->getMemberDecl()->getAsFunction()) + AddIfArgCountChecks(FD); + } + // FIXME: handle function pointers and lambdas. + return ProduceSignatureHelp(*this, S, Results, Args.size(), OpenParLoc); } // Build an overload candidate set based on the functions we find. SourceLocation Loc = Fn->getExprLoc(); OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal); - SmallVector Results; - - Expr *NakedFn = Fn->IgnoreParenCasts(); - if (auto ULE = dyn_cast(NakedFn)) + if (auto ULE = dyn_cast(NakedFn)) { AddOverloadedCallCandidates(ULE, Args, CandidateSet, /*PartialOverloading=*/true); - else if (auto UME = dyn_cast(NakedFn)) { + } else if (auto UME = dyn_cast(NakedFn)) { TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr; if (UME->hasExplicitTemplateArgs()) { UME->copyTemplateArgumentsInto(TemplateArgsBuffer); diff --git a/clang/test/CodeCompletion/call.cpp b/clang/test/CodeCompletion/call.cpp --- a/clang/test/CodeCompletion/call.cpp +++ b/clang/test/CodeCompletion/call.cpp @@ -32,3 +32,17 @@ // CHECK-CC3-NEXT: OVERLOAD: [#void#]f(<#int i#>, int j, int k) // CHECK-CC3-NEXT: OVERLOAD: [#void#]f(<#float x#>, float y) } + +template +void foo(T t) { + f(t, t, t); + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:38:5 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s + // CHECK-CC4: f() + // CHECK-CC4-NEXT: f(<#X#>) + // CHECK-CC4-NEXT: f(<#int i#>, int j, int k) + // CHECK-CC4-NEXT: f(<#float x#>, float y) + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:38:8 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s + // CHECK-CC5-NOT: f() + // CHECK-CC5: f(int i, <#int j#>, int k) + // CHECK-CC5-NEXT: f(float x, <#float y#>) +}