[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/146649 >From 7f8581f01c3c25363b77100635c6df7223badba3 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti Date: Wed, 2 Jul 2025 01:26:25 -0700 Subject: [PATCH 1/2] [clang][CodeComplete] skip explicit obj param in SignatureHelp --- .../clangd/unittests/CodeCompleteTests.cpp| 28 +++ clang/lib/Sema/SemaCodeComplete.cpp | 8 ++ .../skip-explicit-object-parameter.cpp| 24 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index b7c64c7a06745..b18d712ee9aef 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3266,6 +3266,34 @@ TEST(SignatureHelpTest, VariadicType) { } } +TEST(SignatureHelpTest, SkipExplicitObjectParameter) { + Annotations Code(R"cpp( +struct A { + void foo(this auto&& self, int arg); +}; +int main() { + A a {}; + a.foo(^); +} + )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(), +*Preamble, Inputs, MarkupKind::PlainText); + + EXPECT_EQ(1, Result.signatures.size()); + + EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void"))); +} + TEST(CompletionTest, IncludedCompletionKinds) { Annotations Test(R"cpp(#include "^)cpp"); auto TU = TestTU::withCode(Test.code()); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index b5d4a94da83df..0077cf05bd5b0 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -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 diff --git a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp index 55c16bb126fee..0eb71dce95849 100644 --- a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp +++ b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp @@ -6,9 +6,21 @@ int main() { 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 -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: 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 + void foo(this T&& self, int arg); +}; + +int main2() { + B b {}; + b.foo(); +} +// RUN: %clang_cc1 -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) >From a7fae2277ea9267f65ecdfaf05c4b79bcc7a44c4 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti Date: Thu, 3 Jul 2025 02:27:05 -0700 Subject: [PATCH 2/2] Update tests from code review --- .../clangd/unittests/CodeCompleteTests.cpp| 52 ++- .../skip-explicit-object-parameter.cpp| 26 -- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index b18d712ee9aef..5912937e2c80f 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3270,10 +3270,13 @@ 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(^); + a.foo($c1^); + (&A::bar)($c2^); + // TODO: (&A::foo)(^c3) } )cpp"); @@ -3286,12 +3289,22 @@ TEST(SignatureHelpTest, SkipExplicitObjectPara
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
@@ -1,14 +1,42 @@ 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 + 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) MythreyaK wrote: My current understanding is that this line checks that the given overload exists, but not that other overloads do not. Do I need to ensure that this is the only overload? https://github.com/llvm/llvm-project/pull/146649 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
MythreyaK wrote: Seems to be working as expected. I'll add a test case for this as well.   https://github.com/llvm/llvm-project/pull/146649 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
MythreyaK wrote: The completion for `(&A::foo2)` is just `(A, int)`, not `foo(A, int)`. I tried to add a test case, like in this [godbolt](https://godbolt.org/z/77zE7e3oE) example, ```cpp struct A { void foo1(this A self, int arg) {} template void foo2(this T&& self, int arg, float arg2) {} }; int main() { A a {}; (&A::foo1)(a, 1); (&A::foo2)(a, 1, 3.4); // possible? // (&A::foo2)(a, 1 /* , 3.4*/); // causes a compiler crash return 0; } ``` but causes a crash ([example](https://godbolt.org/z/oo198z8Gh)). So I haven't added those cases, but left a `TODO`. Should I leave a `TODO: PR 146649` instead? https://github.com/llvm/llvm-project/pull/146649 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
MythreyaK wrote: > How does completion handle things like this? @cor3ntin Oh I totally forgot about this! Thanks! I'll check it in the evening. https://github.com/llvm/llvm-project/pull/146649 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/146649 >From 7f8581f01c3c25363b77100635c6df7223badba3 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti Date: Wed, 2 Jul 2025 01:26:25 -0700 Subject: [PATCH] [clang][CodeComplete] skip explicit obj param in SignatureHelp --- .../clangd/unittests/CodeCompleteTests.cpp| 28 +++ clang/lib/Sema/SemaCodeComplete.cpp | 8 ++ .../skip-explicit-object-parameter.cpp| 24 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index b7c64c7a06745..b18d712ee9aef 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3266,6 +3266,34 @@ TEST(SignatureHelpTest, VariadicType) { } } +TEST(SignatureHelpTest, SkipExplicitObjectParameter) { + Annotations Code(R"cpp( +struct A { + void foo(this auto&& self, int arg); +}; +int main() { + A a {}; + a.foo(^); +} + )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(), +*Preamble, Inputs, MarkupKind::PlainText); + + EXPECT_EQ(1, Result.signatures.size()); + + EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void"))); +} + TEST(CompletionTest, IncludedCompletionKinds) { Annotations Test(R"cpp(#include "^)cpp"); auto TU = TestTU::withCode(Test.code()); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index b5d4a94da83df..0077cf05bd5b0 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -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 diff --git a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp index 55c16bb126fee..0eb71dce95849 100644 --- a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp +++ b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp @@ -6,9 +6,21 @@ int main() { 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 -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: 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 + void foo(this T&& self, int arg); +}; + +int main2() { + B b {}; + b.foo(); +} +// RUN: %clang_cc1 -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) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)
https://github.com/MythreyaK edited https://github.com/llvm/llvm-project/pull/146649 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits