-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang] Skip suggesting unqualified members in explicit-object member functions #153760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Skip suggesting unqualified members in explicit-object member functions #153760
Conversation
690ca3d to
0af9408
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to add this paren, otherwise test fails
clang/test/CodeCompletion/cpp23-explicit-object.cpp:46:1: error: expected expression
46 | }
| ^
1 error generated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed this file from skip-explicit-object-parameter.cpp to cpp23-explicit-object.cpp and added the test here, instead of creating a new file.
0af9408 to
4953cfb
Compare
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-tools-extra Author: Mythreya Kuricheti (MythreyaK) ChangesFixes #141291 Skip suggesting members when not using [TODO: Need to add a semacodecomplete test too, will add it in a new commit] Full diff: https://github.com/llvm/llvm-project/pull/153760.diff 3 Files Affected:
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()) {
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
|
|
@llvm/pr-subscribers-clangd Author: Mythreya Kuricheti (MythreyaK) ChangesFixes #141291 Skip suggesting members when not using [TODO: Need to add a semacodecomplete test too, will add it in a new commit] Full diff: https://github.com/llvm/llvm-project/pull/153760.diff 3 Files Affected:
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()) {
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
|
clang/lib/Sema/SemaCodeComplete.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think CurContext is the right DeclContext to check here, as it could be a nested context inside the method. For example:
struct A {
int member;
void foo(this A &self) {
[&]() {
// Completion should not offer `member`, but with the current patch it does.
mem^
}();
}
};I think you want something like getFunctionLevelDeclContext(/*AllowLambda=*/false).
Also, as mentioned on Discord, we should do this check in CodeCompleteOrdinaryName() and save the result on the ResultBuilder. This is actually important not just for performance, but also to limit the effect of the change to cases where we're in CodeCompleteOrdinaryName(), since ResultBuilder is used for completions in other contexts as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, yeah, I failed to consider this 🤔.
Is storing it with a call to setObjectTypeQualifiers fine? Or is introducing a new get-set pair better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is storing it with a call to
setObjectTypeQualifiersfine? Or is introducing a new get-set pair better?
I think it would actually make the most sense in an else branch of this if, since getCurrentThisType() should return null in an explicit-object member function (due to this check) (which suggests using a different get/set pair).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we ensure that explicit object methods still participate in overload resolution, since the HasObjectTypeQualifiers is false here?
Example, his does not suggest memberFnB
struct A {
int member {};
int memberFnA(int a);
int memberFnB(this A const& self, float b);
void foo(this const A& self) {
self.mem^ // suggests only 'member', should suggest `memberFnB` as well
self.memberFnA(1); // correctly diagnosed by clangd, but not suggested
}
};Should HasObjectTypeQualifiers be true for explicit object methods? I was thinking we can perhaps infer the qualifiers from the this parameter, and reuse setObjectTypeQualifiers to ensure correct overload resolution 🤔?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, but I would say this is an unrelated bug:
struct A {
int foo(this A const&);
};
void bar(const A& a) {
a.^ // foo not offered
}This scenario involves CodeCompleteMemberReferencExpr, so I don't think it needs to influence what we do in CodeCompleteOrdinaryName. Let's file it as another issue to look at later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This scenario involves CodeCompleteMemberReferencExpr, so I don't think it needs to influence what we do in CodeCompleteOrdinaryName.
Sounds good!
Let's file it as another issue to look at later?
Looks like it's the first part of #109608.
clang/lib/Sema/SemaCodeComplete.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check should apply to methods as well as fields:
struct A {
int method();
void foo(this A &self) {
// Should not offer `method`
me^
}
};2a3b491 to
9b52103
Compare
clang/lib/Sema/SemaCodeComplete.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to rename this, wasn't sure of a good name, especially since filters use the IsXxx format.
9b52103 to
d8cee91
Compare
HighCommander4
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a test case for the nested-scope scenario discussed in this comment please?
(Feel free to pick just one of the libSema and clangd test suites for that one.)
Otherwise looks good!
|
Also, could you file a follow-up issue about the analogous bug for |
|
Does this seem okay?
Thanks for the heads up! Should be in now.
Created #154272. |
HighCommander4
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Fixes #141291
Skip suggesting members when not using
self.in explicit object methods.