[clang][CodeComplete] Consider qualifiers of explicit object parameters in overload suggestions (#154041)
Fixes https://github.com/llvm/llvm-project/issues/109608
This commit is contained in:
parent
e0acf6592b
commit
0977a6d9e7
@ -4532,6 +4532,139 @@ TEST(CompletionTest, MemberAccessInExplicitObjMemfn) {
|
||||
EXPECT_THAT(Result.Completions, ElementsAre());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CompletionTest, ListExplicitObjectOverloads) {
|
||||
Annotations Code(R"cpp(
|
||||
struct S {
|
||||
void foo1(int a);
|
||||
void foo2(int a) const;
|
||||
void foo2(this const S& self, float a);
|
||||
void foo3(this const S& self, int a);
|
||||
void foo4(this S& self, int a);
|
||||
};
|
||||
|
||||
void S::foo1(int a) {
|
||||
this->$c1^;
|
||||
}
|
||||
|
||||
void S::foo2(int a) const {
|
||||
this->$c2^;
|
||||
}
|
||||
|
||||
void S::foo3(this const S& self, int a) {
|
||||
self.$c3^;
|
||||
}
|
||||
|
||||
void S::foo4(this S& self, int a) {
|
||||
self.$c4^;
|
||||
}
|
||||
|
||||
void test1(S s) {
|
||||
s.$c5^;
|
||||
}
|
||||
|
||||
void test2(const S s) {
|
||||
s.$c6^;
|
||||
}
|
||||
)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,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo4"), signature("(int a)"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
{
|
||||
auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"),
|
||||
Preamble.get(), Inputs, Opts);
|
||||
EXPECT_THAT(
|
||||
Result.Completions,
|
||||
UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})")),
|
||||
AllOf(named("foo2"), signature("(float a) const"),
|
||||
snippetSuffix("(${1:float a})")),
|
||||
AllOf(named("foo3"), signature("(int a) const"),
|
||||
snippetSuffix("(${1:int a})"))));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
@ -1435,6 +1435,14 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
|
||||
|
||||
AdjustResultPriorityForDecl(R);
|
||||
|
||||
// Account for explicit object parameter
|
||||
const auto GetQualifiers = [&](const CXXMethodDecl *MethodDecl) {
|
||||
if (MethodDecl->isExplicitObjectMemberFunction())
|
||||
return MethodDecl->getFunctionObjectParameterType().getQualifiers();
|
||||
else
|
||||
return MethodDecl->getMethodQualifiers();
|
||||
};
|
||||
|
||||
if (IsExplicitObjectMemberFunction &&
|
||||
R.Kind == CodeCompletionResult::RK_Declaration &&
|
||||
(isa<CXXMethodDecl>(R.Declaration) || isa<FieldDecl>(R.Declaration))) {
|
||||
@ -1447,7 +1455,7 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
|
||||
if (HasObjectTypeQualifiers)
|
||||
if (const auto *Method = dyn_cast<CXXMethodDecl>(R.Declaration))
|
||||
if (Method->isInstance()) {
|
||||
Qualifiers MethodQuals = Method->getMethodQualifiers();
|
||||
Qualifiers MethodQuals = GetQualifiers(Method);
|
||||
if (ObjectTypeQualifiers == MethodQuals)
|
||||
R.Priority += CCD_ObjectQualifierMatch;
|
||||
else if (ObjectTypeQualifiers - MethodQuals) {
|
||||
@ -3426,42 +3434,57 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
|
||||
Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS));
|
||||
}
|
||||
|
||||
static void
|
||||
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
|
||||
const FunctionDecl *Function) {
|
||||
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
|
||||
if (!Proto || !Proto->getMethodQuals())
|
||||
return;
|
||||
|
||||
static void AddFunctionTypeQuals(CodeCompletionBuilder &Result,
|
||||
const Qualifiers Quals) {
|
||||
// FIXME: Add ref-qualifier!
|
||||
|
||||
// Handle single qualifiers without copying
|
||||
if (Proto->getMethodQuals().hasOnlyConst()) {
|
||||
if (Quals.hasOnlyConst()) {
|
||||
Result.AddInformativeChunk(" const");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Proto->getMethodQuals().hasOnlyVolatile()) {
|
||||
if (Quals.hasOnlyVolatile()) {
|
||||
Result.AddInformativeChunk(" volatile");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Proto->getMethodQuals().hasOnlyRestrict()) {
|
||||
if (Quals.hasOnlyRestrict()) {
|
||||
Result.AddInformativeChunk(" restrict");
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle multiple qualifiers.
|
||||
std::string QualsStr;
|
||||
if (Proto->isConst())
|
||||
if (Quals.hasConst())
|
||||
QualsStr += " const";
|
||||
if (Proto->isVolatile())
|
||||
if (Quals.hasVolatile())
|
||||
QualsStr += " volatile";
|
||||
if (Proto->isRestrict())
|
||||
if (Quals.hasRestrict())
|
||||
QualsStr += " restrict";
|
||||
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
|
||||
}
|
||||
|
||||
static void
|
||||
AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
|
||||
const FunctionDecl *Function) {
|
||||
if (auto *CxxMethodDecl = llvm::dyn_cast_if_present<CXXMethodDecl>(Function);
|
||||
CxxMethodDecl && CxxMethodDecl->hasCXXExplicitFunctionObjectParameter()) {
|
||||
// if explicit object method, infer quals from the object parameter
|
||||
const auto Quals = CxxMethodDecl->getFunctionObjectParameterType();
|
||||
if (!Quals.hasQualifiers())
|
||||
return;
|
||||
|
||||
AddFunctionTypeQuals(Result, Quals.getQualifiers());
|
||||
} else {
|
||||
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
|
||||
if (!Proto || !Proto->getMethodQuals())
|
||||
return;
|
||||
|
||||
AddFunctionTypeQuals(Result, Proto->getMethodQuals());
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AddFunctionExceptSpecToCompletionString(std::string &NameAndSignature,
|
||||
const FunctionDecl *Function) {
|
||||
|
@ -81,3 +81,73 @@ struct C {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct S {
|
||||
void foo1(int a);
|
||||
void foo2(int a) const;
|
||||
void foo2(this const S& self, float a);
|
||||
void foo3(this const S& self, int a);
|
||||
void foo4(this S& self, int a);
|
||||
};
|
||||
|
||||
void S::foo1(int a) {
|
||||
this->;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC7 %s
|
||||
// CHECK-CC7: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
||||
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC7: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
// CHECK-CC7: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
||||
}
|
||||
|
||||
void S::foo2(int a) const {
|
||||
this->;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC8 %s
|
||||
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC8: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
}
|
||||
|
||||
void S::foo3(this const S& self, int a) {
|
||||
self.;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC9 %s
|
||||
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC9: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
}
|
||||
|
||||
void S::foo4(this S& self, int a) {
|
||||
self.;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC10 %s
|
||||
// CHECK-CC10: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
||||
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC10: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
// CHECK-CC10: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
||||
}
|
||||
|
||||
void test1(S s) {
|
||||
s.;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC11 %s
|
||||
// CHECK-CC11: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
||||
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC11: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
// CHECK-CC11: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
||||
}
|
||||
|
||||
void test2(const S s) {
|
||||
s.;
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC12 %s
|
||||
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
||||
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
||||
// CHECK-CC12: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
||||
}
|
||||
|
||||
void test3(S s) {
|
||||
s.foo2();
|
||||
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC13 %s
|
||||
// CHECK-CC13: OVERLOAD: [#void#]foo2(<#int a#>)
|
||||
// CHECK-CC13: OVERLOAD: [#void#]foo2(float a)
|
||||
// TODO: foo2 should be OVERLOAD: [#void#]foo2(<#float a#>)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user