[clang] [clang-tools-extra] [clang] Include explicit object methods in overload suggestions (PR #154041)
Mythreya Kuricheti via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 20 23:01:52 PDT 2025
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/154041
>From 471fa8c60c27a99ce029b1168679a6c5b0271108 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Sat, 16 Aug 2025 15:48:02 -0700
Subject: [PATCH 1/5] [clang] Include explicit object methods in overload
suggestions
---
.../clangd/unittests/CodeCompleteTests.cpp | 120 ++++++++++++++++++
clang/lib/Sema/SemaCodeComplete.cpp | 10 +-
2 files changed, 129 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 3f1623904d8cc..022eca4cf5395 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4532,6 +4532,126 @@ TEST(CompletionTest, MemberAccessInExplicitObjMemfn) {
EXPECT_THAT(Result.Completions, ElementsAre());
}
}
+
+TEST(CompletionTest, ListExplicitObjectOverloads) {
+ Annotations Code(R"cpp(
+ struct S {
+ void foo1(int a);
+ void foo2(int a) const;
+ void foo3(this const S& self, int a);
+ void foo4(this S& self, int a);
+ };
+
+ void S::foo1(int a) {
+ this->$c1^;
+ }
+
+ void S::foo2(int a) const {
+ this->$c2^;
+ }
+
+ void S::foo3(this const S& self, int a) {
+ self.$c3^;
+ }
+
+ void S::foo4(this S& self, int a) {
+ self.$c4^;
+ }
+
+ void test1(S s) {
+ s.$c5^;
+ }
+
+ void test2(const S s) {
+ s.$c6^;
+ }
+ )cpp");
+
+ auto TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs = {"-std=c++23"};
+
+ auto Preamble = TU.preamble();
+ ASSERT_TRUE(Preamble);
+
+ CodeCompleteOptions Opts{};
+
+ MockFS FS;
+ auto Inputs = TU.inputs(FS);
+
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
+ }
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})"))));
+ }
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})"))));
+ }
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
+ }
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
+ }
+ {
+ auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"),
+ Preamble.get(), Inputs, Opts);
+ EXPECT_THAT(Result.Completions,
+ UnorderedElementsAre(
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a)" /* const */),
+ snippetSuffix("(${1:int a})"))));
+ }
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index eb1bbec82b645..5bd1ef72e724c 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -1435,6 +1435,14 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
AdjustResultPriorityForDecl(R);
+ // Account for explicit object parameter
+ const auto getQualifiers = [&](const CXXMethodDecl *MethodDecl) {
+ if (MethodDecl->isExplicitObjectMemberFunction())
+ return MethodDecl->getFunctionObjectParameterType().getQualifiers();
+ else
+ return MethodDecl->getMethodQualifiers();
+ };
+
if (IsExplicitObjectMemberFunction &&
R.Kind == CodeCompletionResult::RK_Declaration &&
(isa<CXXMethodDecl>(R.Declaration) || isa<FieldDecl>(R.Declaration))) {
@@ -1447,7 +1455,7 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
if (HasObjectTypeQualifiers)
if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration))
if (Method->isInstance()) {
- Qualifiers MethodQuals = Method->getMethodQualifiers();
+ Qualifiers MethodQuals = getQualifiers(Method);
if (ObjectTypeQualifiers == MethodQuals)
R.Priority += CCD_ObjectQualifierMatch;
else if (ObjectTypeQualifiers - MethodQuals) {
>From aa2c6fe5d8c6ab6ba492c1dd6800fe816bbb72a6 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Mon, 18 Aug 2025 00:45:13 -0700
Subject: [PATCH 2/5] Add const to signature string
---
.../clangd/unittests/CodeCompleteTests.cpp | 96 +++++++++----------
clang/lib/Sema/SemaCodeComplete.cpp | 27 ++++++
2 files changed, 75 insertions(+), 48 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 022eca4cf5395..5e60cce303bb9 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4581,74 +4581,74 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo1"), signature("(int a)"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo4"), signature("(int a)"),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo1"), signature("(int a)"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo4"), signature("(int a)"),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo1"), signature("(int a)"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo4"), signature("(int a)"),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo4"), signature("(int a)"),
+ snippetSuffix("(${1:int a})"))));
}
{
auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"),
Preamble.get(), Inputs, Opts);
- EXPECT_THAT(Result.Completions,
- UnorderedElementsAre(
- AllOf(named("foo2"), signature("(int a) const"),
- snippetSuffix("(${1:int a})")),
- AllOf(named("foo3"), signature("(int a)" /* const */),
- snippetSuffix("(${1:int a})"))));
+ EXPECT_THAT(
+ Result.Completions,
+ UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})")),
+ AllOf(named("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})"))));
}
}
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 5bd1ef72e724c..28b4900452849 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -3434,9 +3434,36 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS));
}
+// Sets the function qualifiers completion string by inspecting the explicit
+// object
+static void AddCXXExplicitObjectFunctionTypeQualsToCompletionString(
+ CodeCompletionBuilder &Result, const CXXMethodDecl *Function) {
+ const auto Quals = Function->getFunctionObjectParameterType();
+
+ if (!Quals.hasQualifiers())
+ return;
+
+ std::string QualsStr;
+ if (Quals.getQualifiers().hasConst())
+ QualsStr += " const";
+ if (Quals.getQualifiers().hasVolatile())
+ QualsStr += " volatile";
+ if (Quals.getQualifiers().hasRestrict())
+ QualsStr += " restrict";
+ Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
+}
+
static void
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
const FunctionDecl *Function) {
+ if (auto *CxxMethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
+ CxxMethodDecl && CxxMethodDecl->hasCXXExplicitFunctionObjectParameter()) {
+ // if explicit object method, infer quals from the object parameter
+ AddCXXExplicitObjectFunctionTypeQualsToCompletionString(Result,
+ CxxMethodDecl);
+ return;
+ }
+
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
if (!Proto || !Proto->getMethodQuals())
return;
>From 639da07724cc542cd430203226e2d332199f8346 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Mon, 18 Aug 2025 02:05:58 -0700
Subject: [PATCH 3/5] Update tests
---
.../clangd/unittests/CodeCompleteTests.cpp | 13 ++++
.../CodeCompletion/cpp23-explicit-object.cpp | 70 +++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 5e60cce303bb9..7640569128172 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4538,6 +4538,7 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
struct S {
void foo1(int a);
void foo2(int a) const;
+ void foo2(this const S& self, float a);
void foo3(this const S& self, int a);
void foo4(this S& self, int a);
};
@@ -4587,6 +4588,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
snippetSuffix("(${1:int a})")),
AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
AllOf(named("foo4"), signature("(int a)"),
@@ -4599,6 +4602,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
Result.Completions,
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})"))));
}
@@ -4609,6 +4614,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
Result.Completions,
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})"))));
}
@@ -4621,6 +4628,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
snippetSuffix("(${1:int a})")),
AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
AllOf(named("foo4"), signature("(int a)"),
@@ -4635,6 +4644,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
snippetSuffix("(${1:int a})")),
AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
AllOf(named("foo4"), signature("(int a)"),
@@ -4647,6 +4658,8 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
Result.Completions,
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
snippetSuffix("(${1:int a})")),
+ AllOf(named("foo2"), signature("(float a) const"),
+ snippetSuffix("(${1:float a})")),
AllOf(named("foo3"), signature("(int a) const"),
snippetSuffix("(${1:int a})"))));
}
diff --git a/clang/test/CodeCompletion/cpp23-explicit-object.cpp b/clang/test/CodeCompletion/cpp23-explicit-object.cpp
index 08e5565f3d186..ea97237ecd24b 100644
--- a/clang/test/CodeCompletion/cpp23-explicit-object.cpp
+++ b/clang/test/CodeCompletion/cpp23-explicit-object.cpp
@@ -81,3 +81,73 @@ struct C {
}
};
+
+struct S {
+ void foo1(int a);
+ void foo2(int a) const;
+ void foo2(this const S& self, float a);
+ void foo3(this const S& self, int a);
+ void foo4(this S& self, int a);
+};
+
+void S::foo1(int a) {
+ this->;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC7 %s
+// CHECK-CC7: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
+// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC7: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+// CHECK-CC7: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
+}
+
+void S::foo2(int a) const {
+ this->;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC8 %s
+// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC8: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+}
+
+void S::foo3(this const S& self, int a) {
+ self.;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC9 %s
+// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC9: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+}
+
+void S::foo4(this S& self, int a) {
+ self.;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC10 %s
+// CHECK-CC10: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
+// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC10: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+// CHECK-CC10: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
+}
+
+void test1(S s) {
+ s.;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC11 %s
+// CHECK-CC11: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
+// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC11: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+// CHECK-CC11: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
+}
+
+void test2(const S s) {
+ s.;
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC12 %s
+// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
+// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
+// CHECK-CC12: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
+}
+
+void test3(S s) {
+ s.foo2();
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC13 %s
+// CHECK-CC13: OVERLOAD: [#void#]foo2(<#int a#>)
+// CHECK-CC13: OVERLOAD: [#void#]foo2(float a)
+// TODO: foo2 should be OVERLOAD: [#void#]foo2(<#float a#>)
+}
>From d2476ed6018439149b803b94edcdaecf6efbf387 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Wed, 20 Aug 2025 01:48:52 -0700
Subject: [PATCH 4/5] code review
---
clang/lib/Sema/SemaCodeComplete.cpp | 86 ++++++++++++++---------------
1 file changed, 43 insertions(+), 43 deletions(-)
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 28b4900452849..eb27432edb15d 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -413,6 +413,37 @@ class ResultBuilder {
bool IsImpossibleToSatisfy(const NamedDecl *ND) const;
//@}
};
+
+void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
+ const Qualifiers Quals) {
+ // FIXME: Add ref-qualifier!
+
+ // Handle single qualifiers without copying
+ if (Quals.hasOnlyConst()) {
+ Result.AddInformativeChunk(" const");
+ return;
+ }
+
+ if (Quals.hasOnlyVolatile()) {
+ Result.AddInformativeChunk(" volatile");
+ return;
+ }
+
+ if (Quals.hasOnlyRestrict()) {
+ Result.AddInformativeChunk(" restrict");
+ return;
+ }
+
+ // Handle multiple qualifiers.
+ std::string QualsStr;
+ if (Quals.hasConst())
+ QualsStr += " const";
+ if (Quals.hasVolatile())
+ QualsStr += " volatile";
+ if (Quals.hasRestrict())
+ QualsStr += " restrict";
+ Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
+}
} // namespace
void PreferredTypeBuilder::enterReturn(Sema &S, SourceLocation Tok) {
@@ -1436,7 +1467,7 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
AdjustResultPriorityForDecl(R);
// Account for explicit object parameter
- const auto getQualifiers = [&](const CXXMethodDecl *MethodDecl) {
+ const auto GetQualifiers = [&](const CXXMethodDecl *MethodDecl) {
if (MethodDecl->isExplicitObjectMemberFunction())
return MethodDecl->getFunctionObjectParameterType().getQualifiers();
else
@@ -1455,7 +1486,7 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
if (HasObjectTypeQualifiers)
if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration))
if (Method->isInstance()) {
- Qualifiers MethodQuals = getQualifiers(Method);
+ Qualifiers MethodQuals = GetQualifiers(Method);
if (ObjectTypeQualifiers == MethodQuals)
R.Priority += CCD_ObjectQualifierMatch;
else if (ObjectTypeQualifiers - MethodQuals) {
@@ -3438,19 +3469,15 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
// object
static void AddCXXExplicitObjectFunctionTypeQualsToCompletionString(
CodeCompletionBuilder &Result, const CXXMethodDecl *Function) {
- const auto Quals = Function->getFunctionObjectParameterType();
+ assert(isa<CXXMethodDecl>(Function) &&
+ Function->hasCXXExplicitFunctionObjectParameter() &&
+ "Valid only on CXX explicit object methods");
+ const auto Quals = Function->getFunctionObjectParameterType();
if (!Quals.hasQualifiers())
return;
- std::string QualsStr;
- if (Quals.getQualifiers().hasConst())
- QualsStr += " const";
- if (Quals.getQualifiers().hasVolatile())
- QualsStr += " volatile";
- if (Quals.getQualifiers().hasRestrict())
- QualsStr += " restrict";
- Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
+ AddFunctionTypeQuals(Result, Quals.getQualifiers());
}
static void
@@ -3461,40 +3488,13 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
// if explicit object method, infer quals from the object parameter
AddCXXExplicitObjectFunctionTypeQualsToCompletionString(Result,
CxxMethodDecl);
- return;
- }
-
- const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
- if (!Proto || !Proto->getMethodQuals())
- return;
-
- // FIXME: Add ref-qualifier!
-
- // Handle single qualifiers without copying
- if (Proto->getMethodQuals().hasOnlyConst()) {
- Result.AddInformativeChunk(" const");
- return;
- }
-
- if (Proto->getMethodQuals().hasOnlyVolatile()) {
- Result.AddInformativeChunk(" volatile");
- return;
- }
+ } else {
+ const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
+ if (!Proto || !Proto->getMethodQuals())
+ return;
- if (Proto->getMethodQuals().hasOnlyRestrict()) {
- Result.AddInformativeChunk(" restrict");
- return;
+ AddFunctionTypeQuals(Result, Proto->getMethodQuals());
}
-
- // Handle multiple qualifiers.
- std::string QualsStr;
- if (Proto->isConst())
- QualsStr += " const";
- if (Proto->isVolatile())
- QualsStr += " volatile";
- if (Proto->isRestrict())
- QualsStr += " restrict";
- Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
}
static void
>From 888ef93149457b20a1186d0bc4a25a499bf72294 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Wed, 20 Aug 2025 23:00:38 -0700
Subject: [PATCH 5/5] code review
---
clang/lib/Sema/SemaCodeComplete.cpp | 74 ++++++++++++-----------------
1 file changed, 31 insertions(+), 43 deletions(-)
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index eb27432edb15d..c6adead5a65f5 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -413,37 +413,6 @@ class ResultBuilder {
bool IsImpossibleToSatisfy(const NamedDecl *ND) const;
//@}
};
-
-void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
- const Qualifiers Quals) {
- // FIXME: Add ref-qualifier!
-
- // Handle single qualifiers without copying
- if (Quals.hasOnlyConst()) {
- Result.AddInformativeChunk(" const");
- return;
- }
-
- if (Quals.hasOnlyVolatile()) {
- Result.AddInformativeChunk(" volatile");
- return;
- }
-
- if (Quals.hasOnlyRestrict()) {
- Result.AddInformativeChunk(" restrict");
- return;
- }
-
- // Handle multiple qualifiers.
- std::string QualsStr;
- if (Quals.hasConst())
- QualsStr += " const";
- if (Quals.hasVolatile())
- QualsStr += " volatile";
- if (Quals.hasRestrict())
- QualsStr += " restrict";
- Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
-}
} // namespace
void PreferredTypeBuilder::enterReturn(Sema &S, SourceLocation Tok) {
@@ -3465,19 +3434,35 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS));
}
-// Sets the function qualifiers completion string by inspecting the explicit
-// object
-static void AddCXXExplicitObjectFunctionTypeQualsToCompletionString(
- CodeCompletionBuilder &Result, const CXXMethodDecl *Function) {
- assert(isa<CXXMethodDecl>(Function) &&
- Function->hasCXXExplicitFunctionObjectParameter() &&
- "Valid only on CXX explicit object methods");
+static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
+ const Qualifiers Quals) {
+ // FIXME: Add ref-qualifier!
+
+ // Handle single qualifiers without copying
+ if (Quals.hasOnlyConst()) {
+ Result.AddInformativeChunk(" const");
+ return;
+ }
- const auto Quals = Function->getFunctionObjectParameterType();
- if (!Quals.hasQualifiers())
+ if (Quals.hasOnlyVolatile()) {
+ Result.AddInformativeChunk(" volatile");
return;
+ }
- AddFunctionTypeQuals(Result, Quals.getQualifiers());
+ if (Quals.hasOnlyRestrict()) {
+ Result.AddInformativeChunk(" restrict");
+ return;
+ }
+
+ // Handle multiple qualifiers.
+ std::string QualsStr;
+ if (Quals.hasConst())
+ QualsStr += " const";
+ if (Quals.hasVolatile())
+ QualsStr += " volatile";
+ if (Quals.hasRestrict())
+ QualsStr += " restrict";
+ Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
}
static void
@@ -3486,8 +3471,11 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
if (auto *CxxMethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
CxxMethodDecl && CxxMethodDecl->hasCXXExplicitFunctionObjectParameter()) {
// if explicit object method, infer quals from the object parameter
- AddCXXExplicitObjectFunctionTypeQualsToCompletionString(Result,
- CxxMethodDecl);
+ const auto Quals = CxxMethodDecl->getFunctionObjectParameterType();
+ if (!Quals.hasQualifiers())
+ return;
+
+ AddFunctionTypeQuals(Result, Quals.getQualifiers());
} else {
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
if (!Proto || !Proto->getMethodQuals())
More information about the cfe-commits
mailing list