-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[clang][CodeComplete] skip explicit obj param in SignatureHelp #146649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang][CodeComplete] skip explicit obj param in SignatureHelp #146649
Conversation
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang Author: Mythreya Kuricheti (MythreyaK) ChangesFixes clangd/clangd#2284. Seems like the code segment here is similar to the one that I updated in the previous PR #146258. It seems like the code is dissimilar enough in both the function bodies that it probably isn't possible to cleanly move it into a single place to prevent repetition. Added 2 tests. Please let me know if any other improvements can be made! Full diff: https://github.com/llvm/llvm-project/pull/146649.diff 3 Files Affected:
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..97fefd3c93897 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4019,7 +4019,9 @@ static void AddOverloadParameterChunks(
Function ? Function->getNumParams() : Prototype->getNumParams();
for (unsigned P = Start; P != NumParams; ++P) {
- if (Function && Function->getParamDecl(P)->hasDefaultArg() && !InOptional) {
+ const ParmVarDecl *Param = Function->getParamDecl(P);
+
+ if (Function && Param->hasDefaultArg() && !InOptional) {
// When we see an optional default argument, put that argument and
// the remaining default arguments into a new, optional string.
CodeCompletionBuilder Opt(Result.getAllocator(),
@@ -4034,6 +4036,13 @@ 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 (FirstParameter && Param->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)
|
592ba1a
to
7f8581f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the fix!
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-cc1
can be removed.
How does completion handle things like this? struct S {
void f(this S, int);
};
int main() {
(&S::f)(S{}, 0);
} |
@cor3ntin Oh I totally forgot about this! Thanks! I'll check it in the evening. |
The completion for struct A {
void foo1(this A self, int arg) {}
template <typename T>
void foo2(this T&& self, int arg, float arg2) {}
};
int main() {
A a {};
(&A::foo1)(a, 1);
(&A::foo2<A&>)(a, 1, 3.4); // possible?
// (&A::foo2<A&>)(a, 1 /* , 3.4*/); // causes a compiler crash
return 0;
} but causes a crash (example). So I haven't added those cases, but left a |
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
Fixes clangd/clangd#2284.
Seems like the code segment here is similar to the one that I updated in the previous PR #146258. It seems like the code is dissimilar enough in both the function bodies that it probably isn't possible to cleanly move it into a single place to prevent repetition.
My current understanding is that this line checks that the given overload exists, but not that other overloads do not, right? How do I ensure that this is the only overload?
Please let me know if any other improvements can be made!