Skip to content

Commit 0977a6d

Browse files
authored
[clang][CodeComplete] Consider qualifiers of explicit object parameters in overload suggestions (llvm#154041)
Fixes llvm#109608
1 parent e0acf65 commit 0977a6d

File tree

3 files changed

+240
-14
lines changed

3 files changed

+240
-14
lines changed

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

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4532,6 +4532,139 @@ TEST(CompletionTest, MemberAccessInExplicitObjMemfn) {
45324532
EXPECT_THAT(Result.Completions, ElementsAre());
45334533
}
45344534
}
4535+
4536+
TEST(CompletionTest, ListExplicitObjectOverloads) {
4537+
Annotations Code(R"cpp(
4538+
struct S {
4539+
void foo1(int a);
4540+
void foo2(int a) const;
4541+
void foo2(this const S& self, float a);
4542+
void foo3(this const S& self, int a);
4543+
void foo4(this S& self, int a);
4544+
};
4545+
4546+
void S::foo1(int a) {
4547+
this->$c1^;
4548+
}
4549+
4550+
void S::foo2(int a) const {
4551+
this->$c2^;
4552+
}
4553+
4554+
void S::foo3(this const S& self, int a) {
4555+
self.$c3^;
4556+
}
4557+
4558+
void S::foo4(this S& self, int a) {
4559+
self.$c4^;
4560+
}
4561+
4562+
void test1(S s) {
4563+
s.$c5^;
4564+
}
4565+
4566+
void test2(const S s) {
4567+
s.$c6^;
4568+
}
4569+
)cpp");
4570+
4571+
auto TU = TestTU::withCode(Code.code());
4572+
TU.ExtraArgs = {"-std=c++23"};
4573+
4574+
auto Preamble = TU.preamble();
4575+
ASSERT_TRUE(Preamble);
4576+
4577+
CodeCompleteOptions Opts{};
4578+
4579+
MockFS FS;
4580+
auto Inputs = TU.inputs(FS);
4581+
4582+
{
4583+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
4584+
Preamble.get(), Inputs, Opts);
4585+
EXPECT_THAT(
4586+
Result.Completions,
4587+
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
4588+
snippetSuffix("(${1:int a})")),
4589+
AllOf(named("foo2"), signature("(int a) const"),
4590+
snippetSuffix("(${1:int a})")),
4591+
AllOf(named("foo2"), signature("(float a) const"),
4592+
snippetSuffix("(${1:float a})")),
4593+
AllOf(named("foo3"), signature("(int a) const"),
4594+
snippetSuffix("(${1:int a})")),
4595+
AllOf(named("foo4"), signature("(int a)"),
4596+
snippetSuffix("(${1:int a})"))));
4597+
}
4598+
{
4599+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
4600+
Preamble.get(), Inputs, Opts);
4601+
EXPECT_THAT(
4602+
Result.Completions,
4603+
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
4604+
snippetSuffix("(${1:int a})")),
4605+
AllOf(named("foo2"), signature("(float a) const"),
4606+
snippetSuffix("(${1:float a})")),
4607+
AllOf(named("foo3"), signature("(int a) const"),
4608+
snippetSuffix("(${1:int a})"))));
4609+
}
4610+
{
4611+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
4612+
Preamble.get(), Inputs, Opts);
4613+
EXPECT_THAT(
4614+
Result.Completions,
4615+
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
4616+
snippetSuffix("(${1:int a})")),
4617+
AllOf(named("foo2"), signature("(float a) const"),
4618+
snippetSuffix("(${1:float a})")),
4619+
AllOf(named("foo3"), signature("(int a) const"),
4620+
snippetSuffix("(${1:int a})"))));
4621+
}
4622+
{
4623+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"),
4624+
Preamble.get(), Inputs, Opts);
4625+
EXPECT_THAT(
4626+
Result.Completions,
4627+
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
4628+
snippetSuffix("(${1:int a})")),
4629+
AllOf(named("foo2"), signature("(int a) const"),
4630+
snippetSuffix("(${1:int a})")),
4631+
AllOf(named("foo2"), signature("(float a) const"),
4632+
snippetSuffix("(${1:float a})")),
4633+
AllOf(named("foo3"), signature("(int a) const"),
4634+
snippetSuffix("(${1:int a})")),
4635+
AllOf(named("foo4"), signature("(int a)"),
4636+
snippetSuffix("(${1:int a})"))));
4637+
}
4638+
{
4639+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"),
4640+
Preamble.get(), Inputs, Opts);
4641+
EXPECT_THAT(
4642+
Result.Completions,
4643+
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
4644+
snippetSuffix("(${1:int a})")),
4645+
AllOf(named("foo2"), signature("(int a) const"),
4646+
snippetSuffix("(${1:int a})")),
4647+
AllOf(named("foo2"), signature("(float a) const"),
4648+
snippetSuffix("(${1:float a})")),
4649+
AllOf(named("foo3"), signature("(int a) const"),
4650+
snippetSuffix("(${1:int a})")),
4651+
AllOf(named("foo4"), signature("(int a)"),
4652+
snippetSuffix("(${1:int a})"))));
4653+
}
4654+
{
4655+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"),
4656+
Preamble.get(), Inputs, Opts);
4657+
EXPECT_THAT(
4658+
Result.Completions,
4659+
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
4660+
snippetSuffix("(${1:int a})")),
4661+
AllOf(named("foo2"), signature("(float a) const"),
4662+
snippetSuffix("(${1:float a})")),
4663+
AllOf(named("foo3"), signature("(int a) const"),
4664+
snippetSuffix("(${1:int a})"))));
4665+
}
4666+
}
4667+
45354668
} // namespace
45364669
} // namespace clangd
45374670
} // namespace clang

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,14 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
14351435

14361436
AdjustResultPriorityForDecl(R);
14371437

1438+
// Account for explicit object parameter
1439+
const auto GetQualifiers = [&](const CXXMethodDecl *MethodDecl) {
1440+
if (MethodDecl->isExplicitObjectMemberFunction())
1441+
return MethodDecl->getFunctionObjectParameterType().getQualifiers();
1442+
else
1443+
return MethodDecl->getMethodQualifiers();
1444+
};
1445+
14381446
if (IsExplicitObjectMemberFunction &&
14391447
R.Kind == CodeCompletionResult::RK_Declaration &&
14401448
(isa<CXXMethodDecl>(R.Declaration) || isa<FieldDecl>(R.Declaration))) {
@@ -1447,7 +1455,7 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
14471455
if (HasObjectTypeQualifiers)
14481456
if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration))
14491457
if (Method->isInstance()) {
1450-
Qualifiers MethodQuals = Method->getMethodQualifiers();
1458+
Qualifiers MethodQuals = GetQualifiers(Method);
14511459
if (ObjectTypeQualifiers == MethodQuals)
14521460
R.Priority += CCD_ObjectQualifierMatch;
14531461
else if (ObjectTypeQualifiers - MethodQuals) {
@@ -3426,42 +3434,57 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
34263434
Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS));
34273435
}
34283436

3429-
static void
3430-
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
3431-
const FunctionDecl *Function) {
3432-
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
3433-
if (!Proto || !Proto->getMethodQuals())
3434-
return;
3435-
3437+
static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
3438+
const Qualifiers Quals) {
34363439
// FIXME: Add ref-qualifier!
34373440

34383441
// Handle single qualifiers without copying
3439-
if (Proto->getMethodQuals().hasOnlyConst()) {
3442+
if (Quals.hasOnlyConst()) {
34403443
Result.AddInformativeChunk(" const");
34413444
return;
34423445
}
34433446

3444-
if (Proto->getMethodQuals().hasOnlyVolatile()) {
3447+
if (Quals.hasOnlyVolatile()) {
34453448
Result.AddInformativeChunk(" volatile");
34463449
return;
34473450
}
34483451

3449-
if (Proto->getMethodQuals().hasOnlyRestrict()) {
3452+
if (Quals.hasOnlyRestrict()) {
34503453
Result.AddInformativeChunk(" restrict");
34513454
return;
34523455
}
34533456

34543457
// Handle multiple qualifiers.
34553458
std::string QualsStr;
3456-
if (Proto->isConst())
3459+
if (Quals.hasConst())
34573460
QualsStr += " const";
3458-
if (Proto->isVolatile())
3461+
if (Quals.hasVolatile())
34593462
QualsStr += " volatile";
3460-
if (Proto->isRestrict())
3463+
if (Quals.hasRestrict())
34613464
QualsStr += " restrict";
34623465
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
34633466
}
34643467

3468+
static void
3469+
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
3470+
const FunctionDecl *Function) {
3471+
if (auto *CxxMethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
3472+
CxxMethodDecl && CxxMethodDecl->hasCXXExplicitFunctionObjectParameter()) {
3473+
// if explicit object method, infer quals from the object parameter
3474+
const auto Quals = CxxMethodDecl->getFunctionObjectParameterType();
3475+
if (!Quals.hasQualifiers())
3476+
return;
3477+
3478+
AddFunctionTypeQuals(Result, Quals.getQualifiers());
3479+
} else {
3480+
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
3481+
if (!Proto || !Proto->getMethodQuals())
3482+
return;
3483+
3484+
AddFunctionTypeQuals(Result, Proto->getMethodQuals());
3485+
}
3486+
}
3487+
34653488
static void
34663489
AddFunctionExceptSpecToCompletionString(std::string &NameAndSignature,
34673490
const FunctionDecl *Function) {

clang/test/CodeCompletion/cpp23-explicit-object.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,73 @@ struct C {
8181
}
8282
};
8383

84+
85+
struct S {
86+
void foo1(int a);
87+
void foo2(int a) const;
88+
void foo2(this const S& self, float a);
89+
void foo3(this const S& self, int a);
90+
void foo4(this S& self, int a);
91+
};
92+
93+
void S::foo1(int a) {
94+
this->;
95+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC7 %s
96+
// CHECK-CC7: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
97+
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
98+
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
99+
// CHECK-CC7: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
100+
// CHECK-CC7: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
101+
}
102+
103+
void S::foo2(int a) const {
104+
this->;
105+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC8 %s
106+
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
107+
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
108+
// CHECK-CC8: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
109+
}
110+
111+
void S::foo3(this const S& self, int a) {
112+
self.;
113+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC9 %s
114+
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
115+
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
116+
// CHECK-CC9: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
117+
}
118+
119+
void S::foo4(this S& self, int a) {
120+
self.;
121+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC10 %s
122+
// CHECK-CC10: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
123+
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
124+
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
125+
// CHECK-CC10: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
126+
// CHECK-CC10: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
127+
}
128+
129+
void test1(S s) {
130+
s.;
131+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC11 %s
132+
// CHECK-CC11: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
133+
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
134+
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
135+
// CHECK-CC11: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
136+
// CHECK-CC11: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
137+
}
138+
139+
void test2(const S s) {
140+
s.;
141+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC12 %s
142+
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
143+
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
144+
// CHECK-CC12: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
145+
}
146+
147+
void test3(S s) {
148+
s.foo2();
149+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC13 %s
150+
// CHECK-CC13: OVERLOAD: [#void#]foo2(<#int a#>)
151+
// CHECK-CC13: OVERLOAD: [#void#]foo2(float a)
152+
// TODO: foo2 should be OVERLOAD: [#void#]foo2(<#float a#>)
153+
}

0 commit comments

Comments
 (0)