[clang] [clang-tools-extra] [clang] Skip suggesting unqualified members in explicit-object member functions (PR #153760)
Mythreya Kuricheti via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 17 17:06:40 PDT 2025
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 <git at 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 <git at 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 <git at 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#>)
More information about the cfe-commits
mailing list