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 @@ -438,8 +438,6 @@ if (C.SemaResult) { getSignature(*SemaCCS, &S.Signature, &S.SnippetSuffix, C.SemaResult->Kind, C.SemaResult->CursorKind, &Completion.RequiredQualifier); - if (!C.SemaResult->FunctionCanBeCall) - S.SnippetSuffix.clear(); S.ReturnType = getReturnType(*SemaCCS); } else if (C.IndexResult) { S.Signature = std::string(C.IndexResult->Signature); 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 @@ -532,45 +532,57 @@ struct Foo { static int staticMethod(); int method() const; + template + void generic(T); + template + static T staticGeneric(); Foo() { - this->$keepSnippet^ - $keepSnippet^ - Foo::$keepSnippet^ + this->$canBeCall^ + $canBeCall^ + Foo::$canBeCall^ } }; struct Derived : Foo { Derived() { - Foo::$keepSnippet^ + Foo::$canBeCall^ } }; struct OtherClass { OtherClass() { Foo f; - f.$keepSnippet^ - &Foo::$noSnippet^ + f.$canBeCall^ + &Foo::$canNotBeCall^ } }; int main() { Foo f; - f.$keepSnippet^ - &Foo::$noSnippet^ + f.$canBeCall^ + &Foo::$canNotBeCall^ } )cpp"); auto TU = TestTU::withCode(Code.code()); - for (const auto &P : Code.points("noSnippet")) { + for (const auto &P : Code.points("canNotBeCall")) { auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts); EXPECT_THAT(Results.Completions, Contains(AllOf(named("method"), snippetSuffix("")))); + EXPECT_THAT( + Results.Completions, + Contains(AllOf(named("generic"), signature(""), + snippetSuffix("<${1:typename T}, ${2:int U}>")))); } - for (const auto &P : Code.points("keepSnippet")) { + for (const auto &P : Code.points("canBeCall")) { auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts); EXPECT_THAT(Results.Completions, Contains(AllOf(named("method"), snippetSuffix("()")))); + EXPECT_THAT(Results.Completions, + Contains(AllOf( + named("generic"), signature("(T)"), + snippetSuffix("<${1:typename T}, ${2:int U}>(${3:T})")))); } // static method will always keep the snippet @@ -578,6 +590,10 @@ auto Results = completions(TU, P, /*IndexSymbols*/ {}, Opts); EXPECT_THAT(Results.Completions, Contains(AllOf(named("staticMethod"), snippetSuffix("()")))); + EXPECT_THAT( + Results.Completions, + Contains(AllOf(named("staticGeneric"), signature("()"), + snippetSuffix("<${1:typename T}>()")))); } } 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 @@ -1384,7 +1384,11 @@ // dot/arrow member access) and we're not inside that class' scope, // it can't be a call. if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) { - const auto *Method = dyn_cast(R.getDeclaration()); + const NamedDecl *ND = R.getDeclaration(); + if (const auto *FuncTmpl = dyn_cast(ND)) { + ND = FuncTmpl->getTemplatedDecl(); + } + const auto *Method = dyn_cast(ND); if (Method && !Method->isStatic()) { // Find the class scope that we're currently in. // We could e.g. be inside a lambda, so walk up the DeclContext until we @@ -3494,6 +3498,10 @@ AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, Ctx, Policy); AddTypedNameChunk(Ctx, Policy, ND, Result); + // We don't emit parameters (and qualifiers) for context where it can't be a + // call. + if (!FunctionCanBeCall) + return; Result.AddChunk(CodeCompletionString::CK_LeftParen); AddFunctionParameterChunks(PP, Policy, Function, Result); Result.AddChunk(CodeCompletionString::CK_RightParen); @@ -3550,6 +3558,15 @@ } } + if (!FunctionCanBeCall) { + // If this isn't a call, emit all the template arguments + // to disambiguate the (potential) overloads. + Result.AddChunk(CodeCompletionString::CK_LeftAngle); + AddTemplateParameterChunks(Ctx, Policy, FunTmpl, Result); + Result.AddChunk(CodeCompletionString::CK_RightAngle); + return Result.TakeString(); + } + if (LastDeducibleArgument) { // Some of the function template arguments cannot be deduced from a // function call, so we introduce an explicit template argument list diff --git a/clang/test/CodeCompletion/member-access.cpp b/clang/test/CodeCompletion/member-access.cpp --- a/clang/test/CodeCompletion/member-access.cpp +++ b/clang/test/CodeCompletion/member-access.cpp @@ -171,7 +171,7 @@ template void dependentColonColonCompletion() { Template::staticFn(); -// CHECK-CC7: function : [#void#]function() +// CHECK-CC7: function : [#void#]function // CHECK-CC7: Nested : Nested // CHECK-CC7: o1 : [#BaseTemplate#]o1 // CHECK-CC7: o2 : [#BaseTemplate#]o2 diff --git a/clang/test/Index/complete-qualified.cpp b/clang/test/Index/complete-qualified.cpp --- a/clang/test/Index/complete-qualified.cpp +++ b/clang/test/Index/complete-qualified.cpp @@ -16,5 +16,5 @@ // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35) // CHECK-CC1: ClassDecl:{TypedText Foo} (35) -// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )} -// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (80) +// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=} +// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo} (80) diff --git a/clang/unittests/Sema/CodeCompleteTest.cpp b/clang/unittests/Sema/CodeCompleteTest.cpp --- a/clang/unittests/Sema/CodeCompleteTest.cpp +++ b/clang/unittests/Sema/CodeCompleteTest.cpp @@ -60,7 +60,10 @@ for (unsigned I = 0; I < NumResults; ++I) { auto R = Results[I]; if (R.Kind == CodeCompletionResult::RK_Declaration) { - if (const auto *FD = llvm::dyn_cast(R.getDeclaration())) { + auto *ND = R.getDeclaration(); + if (auto *Template = llvm::dyn_cast(ND)) + ND = Template->getTemplatedDecl(); + if (const auto *FD = llvm::dyn_cast(ND)) { CompletedFunctionDecl D; D.Name = FD->getNameAsString(); D.CanBeCall = R.FunctionCanBeCall; @@ -191,6 +194,10 @@ struct Foo { static int staticMethod(); int method() const; + template + void generic(T); + template + static T staticGeneric(); Foo() { this->$canBeCall^ $canBeCall^ @@ -223,12 +230,16 @@ auto Results = CollectCompletedFunctions(Code.code(), P); EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false), canBeCall(true)))); + EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false), + canBeCall(true)))); } for (const auto &P : Code.points("cannotBeCall")) { auto Results = CollectCompletedFunctions(Code.code(), P); EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false), canBeCall(false)))); + EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false), + canBeCall(false)))); } // static method can always be a call @@ -236,6 +247,8 @@ auto Results = CollectCompletedFunctions(Code.code(), P); EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true), canBeCall(true)))); + EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true), + canBeCall(true)))); } }