[clang][CodeComplete] skip explicit obj param when creating signature string (#146649)

Fixes clangd/clangd#2284
This commit is contained in:
Mythreya Kuricheti
2025-07-30 23:12:25 -07:00
committed by GitHub
parent 7410f6d31d
commit 3466cdb769
3 changed files with 135 additions and 14 deletions

View File

@@ -3267,6 +3267,56 @@ TEST(SignatureHelpTest, VariadicType) {
}
}
TEST(SignatureHelpTest, SkipExplicitObjectParameter) {
Annotations Code(R"cpp(
struct A {
void foo(this auto&& self, int arg);
void bar(this A self, int arg);
};
int main() {
A a {};
a.foo($c1^);
(&A::bar)($c2^);
(&A::foo)($c3^);
}
)cpp");
auto TU = TestTU::withCode(Code.code());
TU.ExtraArgs = {"-std=c++23"};
MockFS FS;
auto Inputs = TU.inputs(FS);
auto Preamble = TU.preamble();
ASSERT_TRUE(Preamble);
{
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c1"),
*Preamble, Inputs, MarkupKind::PlainText);
EXPECT_EQ(1U, Result.signatures.size());
EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void")));
}
{
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c2"),
*Preamble, Inputs, MarkupKind::PlainText);
EXPECT_EQ(1U, Result.signatures.size());
EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> void")));
}
{
// TODO: llvm/llvm-project/146649
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c3"),
*Preamble, Inputs, MarkupKind::PlainText);
// TODO: We expect 1 signature here, with this signature
EXPECT_EQ(0U, Result.signatures.size());
// EXPECT_THAT(Result.signatures[0], AllOf(sig("([[auto&&]], [[int]]) ->
// void")));
}
}
TEST(CompletionTest, IncludedCompletionKinds) {
Annotations Test(R"cpp(#include "^)cpp");
auto TU = TestTU::withCode(Test.code());
@@ -4369,14 +4419,24 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
Annotations Code(R"cpp(
struct A {
void foo(this auto&& self, int arg);
void bar(this A self, int arg);
};
int main() {
A a {};
a.^
a.$c1^;
(&A::fo$c2^;
(&A::ba$c3^;
}
)cpp");
// TODO: llvm/llvm-project/146649
// This is incorrect behavior. Correct Result should be a variant of,
// c2: signature = (auto&& self, int arg)
// snippet = (${1: auto&& self}, ${2: int arg})
// c3: signature = (A self, int arg)
// snippet = (${1: A self}, ${2: int arg})
auto TU = TestTU::withCode(Code.code());
TU.ExtraArgs = {"-std=c++23"};
@@ -4387,12 +4447,31 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
MockFS FS;
auto Inputs = TU.inputs(FS);
auto Result = codeComplete(testPath(TU.Filename), Code.point(),
Preamble.get(), Inputs, Opts);
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
Preamble.get(), Inputs, Opts);
EXPECT_THAT(Result.Completions,
ElementsAre(AllOf(named("foo"), signature("(int arg)"),
snippetSuffix("(${1:int arg})"))));
EXPECT_THAT(Result.Completions,
UnorderedElementsAre(AllOf(named("foo"), signature("(int arg)"),
snippetSuffix("(${1:int arg})")),
AllOf(named("bar"), signature("(int arg)"),
snippetSuffix("(${1:int arg})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
Preamble.get(), Inputs, Opts);
EXPECT_THAT(
Result.Completions,
ElementsAre(AllOf(named("foo"), signature("<class self:auto>(int arg)"),
snippetSuffix("<${1:class self:auto}>"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
Preamble.get(), Inputs, Opts);
EXPECT_THAT(Result.Completions,
ElementsAre(AllOf(named("bar"), signature("(int arg)"),
snippetSuffix(""))));
}
}
} // namespace
} // namespace clangd

View File

@@ -4034,6 +4034,14 @@ static void AddOverloadParameterChunks(
return;
}
// C++23 introduces an explicit object parameter, a.k.a. "deducing this"
// Skip it for autocomplete and treat the next parameter as the first
// parameter
if (Function && FirstParameter &&
Function->getParamDecl(P)->isExplicitObjectParameter()) {
continue;
}
if (FirstParameter)
FirstParameter = false;
else

View File

@@ -1,14 +1,48 @@
struct A {
void foo(this A self, int arg);
void foo(this auto&& self, int arg);
void bar(this A self, int arg);
};
int main() {
int func1() {
A a {};
a.
}
// RUN: %clang_cc1 -cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++23 %s | FileCheck %s
// CHECK: COMPLETION: A : A::
// CHECK-NEXT: COMPLETION: foo : [#void#]foo(<#int arg#>)
// CHECK-NEXT: COMPLETION: operator= : [#A &#]operator=(<#const A &#>)
// CHECK-NEXT: COMPLETION: operator= : [#A &#]operator=(<#A &&#>)
// CHECK-NEXT: COMPLETION: ~A : [#void#]~A()
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: COMPLETION: A : A::
// CHECK-NEXT-CC1: COMPLETION: bar : [#void#]bar(<#int arg#>)
// CHECK-NEXT-CC1: COMPLETION: foo : [#void#]foo(<#int arg#>)
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#const A &#>)
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#A &&#>)
// CHECK-NEXT-CC1: COMPLETION: ~A : [#void#]~A()
struct B {
template <typename T>
void foo(this T&& self, int arg);
};
int func2() {
B b {};
b.foo();
}
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC2 %s
// CHECK-CC2: OVERLOAD: [#void#]foo(int arg)
// TODO: llvm/llvm-project/146649
// This is incorrect behavior. Correct Result should be a variant of,
// CC3: should be something like [#void#]foo(<#A self#>, <#int arg#>)
// CC4: should be something like [#void#]bar(<#A self#>, <#int arg#>)
int func3() {
(&A::foo)
(&A::bar)
}
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-3):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s
// CHECK-CC3: COMPLETION: foo : [#void#]foo<<#class self:auto#>>(<#int arg#>)
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-4):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC4 %s
// CHECK-CC4: COMPLETION: bar : [#void#]bar(<#int arg#>)
int func4() {
// TODO (&A::foo)(
(&A::bar)(
}
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC5 %s
// CHECK-CC5: OVERLOAD: [#void#](<#A#>, int)