Skip to content

Commit d0cde32

Browse files
authored
[clang][CodeComplete] Do not suggest unqualified members in explicit-object member functions (#153760)
Fixes #141291
1 parent 32ba045 commit d0cde32

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4473,6 +4473,65 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
44734473
snippetSuffix(""))));
44744474
}
44754475
}
4476+
4477+
TEST(CompletionTest, MemberAccessInExplicitObjMemfn) {
4478+
Annotations Code(R"cpp(
4479+
struct A {
4480+
int member {};
4481+
int memberFnA(int a);
4482+
int memberFnA(this A&, float a);
4483+
4484+
void foo(this A& self) {
4485+
// Should not offer any members here, since
4486+
// it needs to be referenced through `self`.
4487+
mem$c1^;
4488+
// should offer all results
4489+
self.mem$c2^;
4490+
4491+
[&]() {
4492+
// should not offer any results
4493+
mem$c3^;
4494+
}();
4495+
}
4496+
};
4497+
)cpp");
4498+
4499+
auto TU = TestTU::withCode(Code.code());
4500+
TU.ExtraArgs = {"-std=c++23"};
4501+
4502+
auto Preamble = TU.preamble();
4503+
ASSERT_TRUE(Preamble);
4504+
4505+
CodeCompleteOptions Opts{};
4506+
4507+
MockFS FS;
4508+
auto Inputs = TU.inputs(FS);
4509+
4510+
{
4511+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
4512+
Preamble.get(), Inputs, Opts);
4513+
4514+
EXPECT_THAT(Result.Completions, ElementsAre());
4515+
}
4516+
{
4517+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
4518+
Preamble.get(), Inputs, Opts);
4519+
4520+
EXPECT_THAT(
4521+
Result.Completions,
4522+
UnorderedElementsAre(named("member"),
4523+
AllOf(named("memberFnA"), signature("(int a)"),
4524+
snippetSuffix("(${1:int a})")),
4525+
AllOf(named("memberFnA"), signature("(float a)"),
4526+
snippetSuffix("(${1:float a})"))));
4527+
}
4528+
{
4529+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
4530+
Preamble.get(), Inputs, Opts);
4531+
4532+
EXPECT_THAT(Result.Completions, ElementsAre());
4533+
}
4534+
}
44764535
} // namespace
44774536
} // namespace clangd
44784537
} // namespace clang

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ class ResultBuilder {
197197
/// Whether the \p ObjectTypeQualifiers field is active.
198198
bool HasObjectTypeQualifiers;
199199

200+
// Whether the member function is using an explicit object parameter
201+
bool IsExplicitObjectMemberFunction;
202+
200203
/// The selector that we prefer.
201204
Selector PreferredSelector;
202205

@@ -218,8 +221,8 @@ class ResultBuilder {
218221
LookupFilter Filter = nullptr)
219222
: SemaRef(SemaRef), Allocator(Allocator), CCTUInfo(CCTUInfo),
220223
Filter(Filter), AllowNestedNameSpecifiers(false),
221-
HasObjectTypeQualifiers(false), CompletionContext(CompletionContext),
222-
ObjCImplementation(nullptr) {
224+
HasObjectTypeQualifiers(false), IsExplicitObjectMemberFunction(false),
225+
CompletionContext(CompletionContext), ObjCImplementation(nullptr) {
223226
// If this is an Objective-C instance method definition, dig out the
224227
// corresponding implementation.
225228
switch (CompletionContext.getKind()) {
@@ -275,6 +278,10 @@ class ResultBuilder {
275278
HasObjectTypeQualifiers = true;
276279
}
277280

281+
void setExplicitObjectMemberFn(bool IsExplicitObjectFn) {
282+
IsExplicitObjectMemberFunction = IsExplicitObjectFn;
283+
}
284+
278285
/// Set the preferred selector.
279286
///
280287
/// When an Objective-C method declaration result is added, and that
@@ -1428,6 +1435,15 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
14281435

14291436
AdjustResultPriorityForDecl(R);
14301437

1438+
if (IsExplicitObjectMemberFunction &&
1439+
R.Kind == CodeCompletionResult::RK_Declaration &&
1440+
(isa<CXXMethodDecl>(R.Declaration) || isa<FieldDecl>(R.Declaration))) {
1441+
// If result is a member in the context of an explicit-object member
1442+
// function, drop it because it must be accessed through the object
1443+
// parameter
1444+
return;
1445+
}
1446+
14311447
if (HasObjectTypeQualifiers)
14321448
if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration))
14331449
if (Method->isInstance()) {
@@ -4636,12 +4652,19 @@ void SemaCodeCompletion::CodeCompleteOrdinaryName(
46364652
break;
46374653
}
46384654

4639-
// If we are in a C++ non-static member function, check the qualifiers on
4640-
// the member function to filter/prioritize the results list.
46414655
auto ThisType = SemaRef.getCurrentThisType();
4642-
if (!ThisType.isNull())
4656+
if (ThisType.isNull()) {
4657+
// check if function scope is an explicit object function
4658+
if (auto *MethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(
4659+
SemaRef.getCurFunctionDecl()))
4660+
Results.setExplicitObjectMemberFn(
4661+
MethodDecl->isExplicitObjectMemberFunction());
4662+
} else {
4663+
// If we are in a C++ non-static member function, check the qualifiers on
4664+
// the member function to filter/prioritize the results list.
46434665
Results.setObjectTypeQualifiers(ThisType->getPointeeType().getQualifiers(),
46444666
VK_LValue);
4667+
}
46454668

46464669
CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext);
46474670
SemaRef.LookupVisibleDecls(S, SemaRef.LookupOrdinaryName, Consumer,

clang/test/CodeCompletion/skip-explicit-object-parameter.cpp renamed to clang/test/CodeCompletion/cpp23-explicit-object.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,42 @@ int func3() {
4242

4343
int func4() {
4444
// TODO (&A::foo)(
45-
(&A::bar)(
45+
(&A::bar)()
4646
}
4747
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC5 %s
4848
// CHECK-CC5: OVERLOAD: [#void#](<#A#>, int)
49+
50+
struct C {
51+
int member {};
52+
int memberFnA(int a);
53+
int memberFnA(this C&, float a);
54+
55+
void foo(this C& self) {
56+
// Should not offer any members here, since
57+
// it needs to be referenced through `self`.
58+
mem
59+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck --allow-empty %s
60+
// CHECK-NOT: COMPLETION: member : [#int#]member
61+
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
62+
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
63+
}
64+
void bar(this C& self) {
65+
// should offer all results
66+
self.mem
67+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC6 %s
68+
// CHECK-CC6: COMPLETION: member : [#int#]member
69+
// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
70+
// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
71+
}
72+
void baz(this C& self) {
73+
[&]() {
74+
// Should not offer any results
75+
mem
76+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck --allow-empty %s
77+
// CHECK-NOT: COMPLETION: member : [#int#]member
78+
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
79+
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
80+
}();
81+
}
82+
};
83+

0 commit comments

Comments
 (0)