Skip to content

Commit 3466cdb

Browse files
authored
[clang][CodeComplete] skip explicit obj param when creating signature string (#146649)
Fixes clangd/clangd#2284
1 parent 7410f6d commit 3466cdb

File tree

3 files changed

+136
-15
lines changed

3 files changed

+136
-15
lines changed

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,6 +3267,56 @@ TEST(SignatureHelpTest, VariadicType) {
32673267
}
32683268
}
32693269

3270+
TEST(SignatureHelpTest, SkipExplicitObjectParameter) {
3271+
Annotations Code(R"cpp(
3272+
struct A {
3273+
void foo(this auto&& self, int arg);
3274+
void bar(this A self, int arg);
3275+
};
3276+
int main() {
3277+
A a {};
3278+
a.foo($c1^);
3279+
(&A::bar)($c2^);
3280+
(&A::foo)($c3^);
3281+
}
3282+
)cpp");
3283+
3284+
auto TU = TestTU::withCode(Code.code());
3285+
TU.ExtraArgs = {"-std=c++23"};
3286+
3287+
MockFS FS;
3288+
auto Inputs = TU.inputs(FS);
3289+
3290+
auto Preamble = TU.preamble();
3291+
ASSERT_TRUE(Preamble);
3292+
3293+
{
3294+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c1"),
3295+
*Preamble, Inputs, MarkupKind::PlainText);
3296+
3297+
EXPECT_EQ(1U, Result.signatures.size());
3298+
3299+
EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void")));
3300+
}
3301+
{
3302+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c2"),
3303+
*Preamble, Inputs, MarkupKind::PlainText);
3304+
3305+
EXPECT_EQ(1U, Result.signatures.size());
3306+
3307+
EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> void")));
3308+
}
3309+
{
3310+
// TODO: llvm/llvm-project/146649
3311+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c3"),
3312+
*Preamble, Inputs, MarkupKind::PlainText);
3313+
// TODO: We expect 1 signature here, with this signature
3314+
EXPECT_EQ(0U, Result.signatures.size());
3315+
// EXPECT_THAT(Result.signatures[0], AllOf(sig("([[auto&&]], [[int]]) ->
3316+
// void")));
3317+
}
3318+
}
3319+
32703320
TEST(CompletionTest, IncludedCompletionKinds) {
32713321
Annotations Test(R"cpp(#include "^)cpp");
32723322
auto TU = TestTU::withCode(Test.code());
@@ -4369,14 +4419,24 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
43694419
Annotations Code(R"cpp(
43704420
struct A {
43714421
void foo(this auto&& self, int arg);
4422+
void bar(this A self, int arg);
43724423
};
43734424
43744425
int main() {
43754426
A a {};
4376-
a.^
4427+
a.$c1^;
4428+
(&A::fo$c2^;
4429+
(&A::ba$c3^;
43774430
}
43784431
)cpp");
43794432

4433+
// TODO: llvm/llvm-project/146649
4434+
// This is incorrect behavior. Correct Result should be a variant of,
4435+
// c2: signature = (auto&& self, int arg)
4436+
// snippet = (${1: auto&& self}, ${2: int arg})
4437+
// c3: signature = (A self, int arg)
4438+
// snippet = (${1: A self}, ${2: int arg})
4439+
43804440
auto TU = TestTU::withCode(Code.code());
43814441
TU.ExtraArgs = {"-std=c++23"};
43824442

@@ -4387,12 +4447,31 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
43874447

43884448
MockFS FS;
43894449
auto Inputs = TU.inputs(FS);
4390-
auto Result = codeComplete(testPath(TU.Filename), Code.point(),
4391-
Preamble.get(), Inputs, Opts);
4392-
4393-
EXPECT_THAT(Result.Completions,
4394-
ElementsAre(AllOf(named("foo"), signature("(int arg)"),
4395-
snippetSuffix("(${1:int arg})"))));
4450+
{
4451+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
4452+
Preamble.get(), Inputs, Opts);
4453+
4454+
EXPECT_THAT(Result.Completions,
4455+
UnorderedElementsAre(AllOf(named("foo"), signature("(int arg)"),
4456+
snippetSuffix("(${1:int arg})")),
4457+
AllOf(named("bar"), signature("(int arg)"),
4458+
snippetSuffix("(${1:int arg})"))));
4459+
}
4460+
{
4461+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
4462+
Preamble.get(), Inputs, Opts);
4463+
EXPECT_THAT(
4464+
Result.Completions,
4465+
ElementsAre(AllOf(named("foo"), signature("<class self:auto>(int arg)"),
4466+
snippetSuffix("<${1:class self:auto}>"))));
4467+
}
4468+
{
4469+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
4470+
Preamble.get(), Inputs, Opts);
4471+
EXPECT_THAT(Result.Completions,
4472+
ElementsAre(AllOf(named("bar"), signature("(int arg)"),
4473+
snippetSuffix(""))));
4474+
}
43964475
}
43974476
} // namespace
43984477
} // namespace clangd

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,6 +4034,14 @@ static void AddOverloadParameterChunks(
40344034
return;
40354035
}
40364036

4037+
// C++23 introduces an explicit object parameter, a.k.a. "deducing this"
4038+
// Skip it for autocomplete and treat the next parameter as the first
4039+
// parameter
4040+
if (Function && FirstParameter &&
4041+
Function->getParamDecl(P)->isExplicitObjectParameter()) {
4042+
continue;
4043+
}
4044+
40374045
if (FirstParameter)
40384046
FirstParameter = false;
40394047
else
Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,48 @@
11
struct A {
2-
void foo(this A self, int arg);
2+
void foo(this auto&& self, int arg);
3+
void bar(this A self, int arg);
34
};
45

5-
int main() {
6+
int func1() {
67
A a {};
78
a.
89
}
9-
// RUN: %clang_cc1 -cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++23 %s | FileCheck %s
10-
// CHECK: COMPLETION: A : A::
11-
// CHECK-NEXT: COMPLETION: foo : [#void#]foo(<#int arg#>)
12-
// CHECK-NEXT: COMPLETION: operator= : [#A &#]operator=(<#const A &#>)
13-
// CHECK-NEXT: COMPLETION: operator= : [#A &#]operator=(<#A &&#>)
14-
// CHECK-NEXT: COMPLETION: ~A : [#void#]~A()
10+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC1 %s
11+
// CHECK-CC1: COMPLETION: A : A::
12+
// CHECK-NEXT-CC1: COMPLETION: bar : [#void#]bar(<#int arg#>)
13+
// CHECK-NEXT-CC1: COMPLETION: foo : [#void#]foo(<#int arg#>)
14+
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#const A &#>)
15+
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#A &&#>)
16+
// CHECK-NEXT-CC1: COMPLETION: ~A : [#void#]~A()
17+
18+
struct B {
19+
template <typename T>
20+
void foo(this T&& self, int arg);
21+
};
22+
23+
int func2() {
24+
B b {};
25+
b.foo();
26+
}
27+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC2 %s
28+
// CHECK-CC2: OVERLOAD: [#void#]foo(int arg)
29+
30+
// TODO: llvm/llvm-project/146649
31+
// This is incorrect behavior. Correct Result should be a variant of,
32+
// CC3: should be something like [#void#]foo(<#A self#>, <#int arg#>)
33+
// CC4: should be something like [#void#]bar(<#A self#>, <#int arg#>)
34+
int func3() {
35+
(&A::foo)
36+
(&A::bar)
37+
}
38+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-3):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s
39+
// CHECK-CC3: COMPLETION: foo : [#void#]foo<<#class self:auto#>>(<#int arg#>)
40+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-4):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC4 %s
41+
// CHECK-CC4: COMPLETION: bar : [#void#]bar(<#int arg#>)
42+
43+
int func4() {
44+
// TODO (&A::foo)(
45+
(&A::bar)(
46+
}
47+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC5 %s
48+
// CHECK-CC5: OVERLOAD: [#void#](<#A#>, int)

0 commit comments

Comments
 (0)