https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/146649
>From 816845b3402afc67acf641778dd7f937d9b1dc53 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Wed, 2 Jul 2025 01:26:25 -0700 Subject: [PATCH 1/3] [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 5a5d815076e2a..f8eaf7787717a 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3267,6 +3267,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 a43ac9eb7610d..0de55800ccdd1 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 <typename T> + 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 caf4482069ab51b5fe178cd270850c79dd1579bd Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Thu, 3 Jul 2025 02:27:05 -0700 Subject: [PATCH 2/3] 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 f8eaf7787717a..b1db0714eab96 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3271,10 +3271,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"); @@ -3287,12 +3290,22 @@ TEST(SignatureHelpTest, SkipExplicitObjectParameter) { auto Preamble = TU.preamble(); ASSERT_TRUE(Preamble); - const auto Result = signatureHelp(testPath(TU.Filename), Code.point(), - *Preamble, Inputs, MarkupKind::PlainText); + { + const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c1"), + *Preamble, Inputs, MarkupKind::PlainText); + + EXPECT_EQ(1, 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(1, Result.signatures.size()); + EXPECT_EQ(1, Result.signatures.size()); - EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void"))); + EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> void"))); + } } TEST(CompletionTest, IncludedCompletionKinds) { @@ -4397,11 +4410,14 @@ 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^s + (&A::ba$c2^; + // TODO: (&A::fo$c3^ } )cpp"); @@ -4415,12 +4431,24 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { MockFS FS; auto Inputs = TU.inputs(FS); - auto Result = codeComplete(testPath(TU.Filename), Code.point(), - Preamble.get(), Inputs, Opts); - - EXPECT_THAT(Result.Completions, - ElementsAre(AllOf(named("foo"), signature("(int arg)"), - snippetSuffix("(${1:int arg})")))); + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"), + Preamble.get(), Inputs, Opts); + + 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); + // TODO: snippet suffix is empty for c2 + EXPECT_THAT(Result.Completions, + ElementsAre(AllOf(named("bar"), signature("(int arg)"), + snippetSuffix("")))); + } } } // namespace } // namespace clangd diff --git a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp index 0eb71dce95849..54754746fe897 100644 --- a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp +++ b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp @@ -1,13 +1,15 @@ 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 -check-prefix=CHECK-CC1 %s +// 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 &&#>) @@ -18,9 +20,23 @@ struct B { void foo(this T&& self, int arg); }; -int main2() { +int func2() { 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 +// 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) + +int func3() { + // TODO: (&A::foo) + (&A::bar) +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: 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-CC4 %s +// CHECK-CC4: OVERLOAD: [#void#](<#A#>, int) >From e08005e3a6fbd42e7063cffe27ce1ccdf0c36d1a Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Tue, 29 Jul 2025 02:59:33 -0700 Subject: [PATCH 3/3] rebase updates --- .../clangd/unittests/CodeCompleteTests.cpp | 37 ++++++++++++++----- .../skip-explicit-object-parameter.cpp | 14 ++++--- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index b1db0714eab96..87550cffaa562 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3277,7 +3277,7 @@ TEST(SignatureHelpTest, SkipExplicitObjectParameter) { A a {}; a.foo($c1^); (&A::bar)($c2^); - // TODO: (&A::foo)(^c3) + (&A::foo)($c3^); } )cpp"); @@ -3294,7 +3294,7 @@ TEST(SignatureHelpTest, SkipExplicitObjectParameter) { const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c1"), *Preamble, Inputs, MarkupKind::PlainText); - EXPECT_EQ(1, Result.signatures.size()); + EXPECT_EQ(1U, Result.signatures.size()); EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void"))); } @@ -3302,10 +3302,19 @@ TEST(SignatureHelpTest, SkipExplicitObjectParameter) { const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c2"), *Preamble, Inputs, MarkupKind::PlainText); - EXPECT_EQ(1, Result.signatures.size()); + EXPECT_EQ(1U, Result.signatures.size()); EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> void"))); } + { + const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c3"), + *Preamble, Inputs, MarkupKind::PlainText); + // TODO: We expect 1 signature here + // EXPECT_EQ(1U, Result.signatures.size()); + + // EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> + // void"))); + } } TEST(CompletionTest, IncludedCompletionKinds) { @@ -4415,9 +4424,9 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { int main() { A a {}; - a.$c1^s + a.$c1^ (&A::ba$c2^; - // TODO: (&A::fo$c3^ + (&A::fo$c3^; } )cpp"); @@ -4435,11 +4444,11 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"), Preamble.get(), Inputs, Opts); - 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})")))); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre( + AllOf(named("foo"), signature("(int arg)"), snippetSuffix("")), + AllOf(named("bar"), signature("(int arg)"), snippetSuffix("")))); } { auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"), @@ -4449,6 +4458,14 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { ElementsAre(AllOf(named("bar"), signature("(int arg)"), snippetSuffix("")))); } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + ElementsAre(AllOf(named("foo"), signature("<class self:auto>(int arg)"), + snippetSuffix("<${1:class self:auto}>")))); + } } } // namespace } // namespace clangd diff --git a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp index 54754746fe897..0301ac0f76db7 100644 --- a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp +++ b/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp @@ -28,15 +28,17 @@ int func2() { // CHECK-CC2: OVERLOAD: [#void#]foo(int arg) int func3() { - // TODO: (&A::foo) + (&A::foo) (&A::bar) } -// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s -// CHECK-CC3: COMPLETION: bar : [#void#]bar(<#int arg#>) +// 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)( + // 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-CC4 %s -// CHECK-CC4: OVERLOAD: [#void#](<#A#>, int) +// 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) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits