https://github.com/MythreyaK updated https://github.com/llvm/llvm-project/pull/153760
>From b279e4b718488a08d25fdc43edae965c0fec288c Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Fri, 15 Aug 2025 00:52:37 -0700 Subject: [PATCH 1/3] [clang] Skip unqualified members in explicit-member functions --- .../clangd/unittests/CodeCompleteTests.cpp | 39 +++++++++++++++++++ clang/lib/Sema/SemaCodeComplete.cpp | 10 +++++ 2 files changed, 49 insertions(+) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 1a1c32c241602..360693a02a1f5 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -4473,6 +4473,45 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { snippetSuffix("")))); } } + +TEST(CompletionTest, MemberAccessInExplicitObjMemfn) { + Annotations Code(R"cpp( + struct A { + int member {}; + + void foo(this A& self) { + // Should not offer `member` here, since it needs to be + // referenced as `self.member`. + mem$c1^; + self.mem$c2^; + } + }; + )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, ElementsAre()); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"), + Preamble.get(), Inputs, Opts); + + EXPECT_THAT(Result.Completions, ElementsAre(named("member"))); + } +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index e4f276086af25..224d105c313e6 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1428,6 +1428,16 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, AdjustResultPriorityForDecl(R); + if (isa<FieldDecl>(R.Declaration)) { + // If result is a member in the context of an explicit-object member + // function, drop it because it must be accessed through the object + // parameter + if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(CurContext); + MethodDecl && MethodDecl->isExplicitObjectMemberFunction()) { + return; + } + } + if (HasObjectTypeQualifiers) if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration)) if (Method->isInstance()) { >From 650760be8da5df048556e6128acfaa788f7d0c38 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Fri, 15 Aug 2025 01:43:48 -0700 Subject: [PATCH 2/3] Add codecomplete lit test --- ...parameter.cpp => cpp23-explicit-object.cpp} | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) rename clang/test/CodeCompletion/{skip-explicit-object-parameter.cpp => cpp23-explicit-object.cpp} (75%) diff --git a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp b/clang/test/CodeCompletion/cpp23-explicit-object.cpp similarity index 75% rename from clang/test/CodeCompletion/skip-explicit-object-parameter.cpp rename to clang/test/CodeCompletion/cpp23-explicit-object.cpp index 587d6cb044d19..c903e2dbbf0a9 100644 --- a/clang/test/CodeCompletion/skip-explicit-object-parameter.cpp +++ b/clang/test/CodeCompletion/cpp23-explicit-object.cpp @@ -42,7 +42,23 @@ int func3() { int func4() { // TODO (&A::foo)( - (&A::bar)( + (&A::bar)() } // 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) + +struct C { + int member {}; + void foo(this C& self) { + // Should not offer `member` here, since it needs to be + // referenced as `self.member`. + mem + } + void bar(this C& self) { + self.mem + } +}; +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-8):8 -std=c++23 %s | FileCheck --allow-empty %s +// CHECK-NOT: COMPLETION: member : [#int#]member +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC6 %s +// CHECK-CC6: COMPLETION: member : [#int#]member >From d8cee9131b6408fb69011ddc407522e2b4c95690 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Sat, 16 Aug 2025 19:07:49 -0700 Subject: [PATCH 3/3] code review --- .../clangd/unittests/CodeCompleteTests.cpp | 15 +++++++-- clang/lib/Sema/SemaCodeComplete.cpp | 33 +++++++++++++------ .../CodeCompletion/cpp23-explicit-object.cpp | 16 ++++++--- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 360693a02a1f5..db41997cf65e0 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -4478,11 +4478,14 @@ TEST(CompletionTest, MemberAccessInExplicitObjMemfn) { Annotations Code(R"cpp( struct A { int member {}; + int memberFnA(int a); + int memberFnA(this A&, float a); void foo(this A& self) { - // Should not offer `member` here, since it needs to be - // referenced as `self.member`. + // Should not offer any members here, since + // it needs to be referenced through `self`. mem$c1^; + // should offer all results self.mem$c2^; } }; @@ -4509,7 +4512,13 @@ TEST(CompletionTest, MemberAccessInExplicitObjMemfn) { auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"), Preamble.get(), Inputs, Opts); - EXPECT_THAT(Result.Completions, ElementsAre(named("member"))); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(named("member"), + AllOf(named("memberFnA"), signature("(int a)"), + snippetSuffix("(${1:int a})")), + AllOf(named("memberFnA"), signature("(float a)"), + snippetSuffix("(${1:float a})")))); } } } // namespace diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 224d105c313e6..eb1bbec82b645 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -197,6 +197,9 @@ class ResultBuilder { /// Whether the \p ObjectTypeQualifiers field is active. bool HasObjectTypeQualifiers; + // Whether the member function is using an explicit object parameter + bool IsExplicitObjectMemberFunction; + /// The selector that we prefer. Selector PreferredSelector; @@ -218,8 +221,8 @@ class ResultBuilder { LookupFilter Filter = nullptr) : SemaRef(SemaRef), Allocator(Allocator), CCTUInfo(CCTUInfo), Filter(Filter), AllowNestedNameSpecifiers(false), - HasObjectTypeQualifiers(false), CompletionContext(CompletionContext), - ObjCImplementation(nullptr) { + HasObjectTypeQualifiers(false), IsExplicitObjectMemberFunction(false), + CompletionContext(CompletionContext), ObjCImplementation(nullptr) { // If this is an Objective-C instance method definition, dig out the // corresponding implementation. switch (CompletionContext.getKind()) { @@ -275,6 +278,10 @@ class ResultBuilder { HasObjectTypeQualifiers = true; } + void setExplicitObjectMemberFn(bool IsExplicitObjectFn) { + IsExplicitObjectMemberFunction = IsExplicitObjectFn; + } + /// Set the preferred selector. /// /// When an Objective-C method declaration result is added, and that @@ -1428,14 +1435,13 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, AdjustResultPriorityForDecl(R); - if (isa<FieldDecl>(R.Declaration)) { + if (IsExplicitObjectMemberFunction && + R.Kind == CodeCompletionResult::RK_Declaration && + (isa<CXXMethodDecl>(R.Declaration) || isa<FieldDecl>(R.Declaration))) { // If result is a member in the context of an explicit-object member // function, drop it because it must be accessed through the object // parameter - if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(CurContext); - MethodDecl && MethodDecl->isExplicitObjectMemberFunction()) { - return; - } + return; } if (HasObjectTypeQualifiers) @@ -4646,12 +4652,19 @@ void SemaCodeCompletion::CodeCompleteOrdinaryName( break; } - // If we are in a C++ non-static member function, check the qualifiers on - // the member function to filter/prioritize the results list. auto ThisType = SemaRef.getCurrentThisType(); - if (!ThisType.isNull()) + if (ThisType.isNull()) { + // check if function scope is an explicit object function + if (auto *MethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>( + SemaRef.getCurFunctionDecl())) + Results.setExplicitObjectMemberFn( + MethodDecl->isExplicitObjectMemberFunction()); + } else { + // If we are in a C++ non-static member function, check the qualifiers on + // the member function to filter/prioritize the results list. Results.setObjectTypeQualifiers(ThisType->getPointeeType().getQualifiers(), VK_LValue); + } CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext); SemaRef.LookupVisibleDecls(S, SemaRef.LookupOrdinaryName, Consumer, diff --git a/clang/test/CodeCompletion/cpp23-explicit-object.cpp b/clang/test/CodeCompletion/cpp23-explicit-object.cpp index c903e2dbbf0a9..2a40185934a05 100644 --- a/clang/test/CodeCompletion/cpp23-explicit-object.cpp +++ b/clang/test/CodeCompletion/cpp23-explicit-object.cpp @@ -49,16 +49,24 @@ int func4() { struct C { int member {}; + int memberFnA(int a); + int memberFnA(this C&, float a); + void foo(this C& self) { - // Should not offer `member` here, since it needs to be - // referenced as `self.member`. + // Should not offer any members here, since + // it needs to be referenced through `self`. mem } void bar(this C& self) { + // should offer all results self.mem } }; -// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-8):8 -std=c++23 %s | FileCheck --allow-empty %s +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-7):8 -std=c++23 %s | FileCheck --allow-empty %s // CHECK-NOT: COMPLETION: member : [#int#]member -// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC6 %s +// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>) +// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>) +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-7):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC6 %s // CHECK-CC6: COMPLETION: member : [#int#]member +// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>) +// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits