[clang] [clang-tools-extra] [clang][CodeComplete] skip explicit obj param in SignatureHelp (PR #146649)

Mythreya Kuricheti via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 29 02:59:53 PDT 2025


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 <git at 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 <git at 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 <git at 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)



More information about the cfe-commits mailing list