[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
Sun Aug 17 23:04:02 PDT 2025
https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/154041
>From 8df889319e8146db958d027d5dee8ed22d6c9d3c 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/2] [clang] Include explicit object methods in overload
suggestions
---
.../clangd/unittests/CodeCompleteTests.cpp | 119 ++++++++++++++++++
clang/lib/Sema/SemaCodeComplete.cpp | 10 +-
2 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 1a1c32c241602..f5989eca4e1b5 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4473,6 +4473,125 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
snippetSuffix(""))));
}
}
+
+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 e4f276086af25..d17b46f8b4070 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -1428,10 +1428,18 @@ 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 (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 3e2be0e0397e74aff6e20ff7c69f0fce4a1fb069 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <git at mythreya.dev>
Date: Sun, 17 Aug 2025 23:03:34 -0700
Subject: [PATCH 2/2] Add const to signature string
---
.../clangd/unittests/CodeCompleteTests.cpp | 96 +++++++++----------
clang/lib/Sema/SemaCodeComplete.cpp | 28 ++++--
2 files changed, 69 insertions(+), 55 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index f5989eca4e1b5..cf07e11d6441e 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4522,74 +4522,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})"))));
}
}
} // namespace
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index d17b46f8b4070..56fac750637b0 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -3422,34 +3422,48 @@ static void
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
const FunctionDecl *Function) {
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
- if (!Proto || !Proto->getMethodQuals())
+
+ // If CXX method, we need to check if explicit object is being used
+ const auto *cxxMethodDecl =
+ llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
+
+ if ((!Proto || !Proto->getMethodQuals()) && !cxxMethodDecl)
return;
// FIXME: Add ref-qualifier!
+ Qualifiers FunctionQuals{};
+
+ if (cxxMethodDecl && cxxMethodDecl->isExplicitObjectMemberFunction()) {
+ FunctionQuals =
+ cxxMethodDecl->getFunctionObjectParameterType().getQualifiers();
+ } else {
+ FunctionQuals = Proto->getMethodQuals();
+ }
+
// Handle single qualifiers without copying
- if (Proto->getMethodQuals().hasOnlyConst()) {
+ if (FunctionQuals.hasOnlyConst()) {
Result.AddInformativeChunk(" const");
return;
}
- if (Proto->getMethodQuals().hasOnlyVolatile()) {
+ if (FunctionQuals.hasOnlyVolatile()) {
Result.AddInformativeChunk(" volatile");
return;
}
- if (Proto->getMethodQuals().hasOnlyRestrict()) {
+ if (FunctionQuals.hasOnlyRestrict()) {
Result.AddInformativeChunk(" restrict");
return;
}
// Handle multiple qualifiers.
std::string QualsStr;
- if (Proto->isConst())
+ if (FunctionQuals.hasConst())
QualsStr += " const";
- if (Proto->isVolatile())
+ if (FunctionQuals.hasVolatile())
QualsStr += " volatile";
- if (Proto->isRestrict())
+ if (FunctionQuals.hasRestrict())
QualsStr += " restrict";
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
}
More information about the cfe-commits
mailing list