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 <g...@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 bf3d8e05c1a4b6d5481980788b3e1a8f10174ea0 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Mon, 18 Aug 2025 00:45:13 -0700 Subject: [PATCH 2/2] 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 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..e56eee287bf8d 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -3418,9 +3418,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(25, '\0'); // longest size + 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)) { + // 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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits