-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang] Include explicit object methods in overload suggestions #154041
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] Include explicit object methods in overload suggestions #154041
Conversation
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.
Test slightly modified from example that @zwuis originally shared in the ticket.
Happy to change or update!
d730c1f
to
8df8893
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.
Using a lambda for now, but we can probably reuse this in other functions as well. Not 100% sure if getFunctionObjectParameterType().getQualifiers()
is the right way to check 🤔.
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.
FWIW, I tried to evaluate whether it would be appropriate for CXXMethodDecl::getMethodQualifiers()
itself to behave this way, but the code at the call sites of that method in actual semantic analysis code is going a bit over my head. I think keeping this logic local to this call site is fine for now.
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 think changing CXXMethodDecl::getMethodQualifiers()
would break name mangling.
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.
Tried it out, here's the diff (b/w this and the new PR) and the corresponding draft PR.
ae6d92a
to
9f149ac
Compare
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-tools-extra Author: Mythreya Kuricheti (MythreyaK) ChangesFix for #109608. Draft to gather feedback. Signature help still needs fixing--signature for void Include methods that use explicit object in code complete suggestions. struct S {
void foo1() const;
void foo2();
void foo3(this const S& self);
void foo4(this S& self);
};
int foo(const S arg) {
arg.f^ // Now suggests foo3 as well
} Full diff: https://github.com/llvm/llvm-project/pull/154041.diff 2 Files Affected:
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 1a1c32c241602..cf07e11d6441e 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -4473,6 +4473,125 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
snippetSuffix(""))));
}
}
+
+TEST(CompletionTest, ListExplicitObjectOverloads) {
+ Annotations Code(R"cpp(
+ struct S {
+ void foo1(int a);
+ void foo2(int a) const;
+ 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("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("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("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("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("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("foo3"), signature("(int a) const"),
+ snippetSuffix("(${1:int a})"))));
+ }
+}
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index e4f276086af25..af900c777a707 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -1428,10 +1428,18 @@ 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 (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) {
@@ -3410,9 +3418,36 @@ static void AddQualifierToCompletionString(CodeCompletionBuilder &Result,
Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS));
}
+// Sets the function qualifiers completion string by inspecting the explicit
+// object
+static void AddCXXExplicitObjectFunctionTypeQualsToCompletionString(
+ CodeCompletionBuilder &Result, const CXXMethodDecl *Function) {
+ const auto Quals = Function->getFunctionObjectParameterType();
+
+ if (!Quals.hasQualifiers())
+ return;
+
+ std::string QualsStr;
+ if (Quals.getQualifiers().hasConst())
+ QualsStr += " const";
+ if (Quals.getQualifiers().hasVolatile())
+ QualsStr += " volatile";
+ if (Quals.getQualifiers().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
+ AddCXXExplicitObjectFunctionTypeQualsToCompletionString(Result,
+ CxxMethodDecl);
+ return;
+ }
+
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
if (!Proto || !Proto->getMethodQuals())
return;
|
524cde5
to
304ab86
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.
Need to check why foo2
is broken. But autocomplete seems to work fine 🤔
foo2(float a) vs [incorrect?]
foo2(<#float a#>) [expected?]
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.
nit: as a local variable, the lambda name should be UpperCamelCase (I know this is counterintuitive)
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.
Wasn't sure which way to go here 😆, done!
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.
This is a nice touch!
One minor comment: could we refactor this to reuse code between this function, and the rest of AddFunctionTypeQualsToCompletionString
? (That way, the "Handle single qualifiers without copying" optimization would apply here 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.
After the refactor for code-reuse, it feels like the body of AddCXXExplicitObjectFunctionTypeQualsToCompletionString
can be moved back into AddFunctionTypeQualsToCompletionString
, to make it similar to the code in else
branch.
I think either is fine, leaving this as is or moving the body back into AddFunctionTypeQualsToCompletionString
, let me know what you think.
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.
Moving it back seems like it might be a bit nicer
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.
Done
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.
FWIW, I tried to evaluate whether it would be appropriate for CXXMethodDecl::getMethodQualifiers()
itself to behave this way, but the code at the call sites of that method in actual semantic analysis code is going a bit over my head. I think keeping this logic local to this call site is fine for now.
2946340
to
83c35d9
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.
Thanks for the update, just minor comments remaining and then I think this is good to merge.
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.
nit: can we move this closer to where it's used? (we can make it static
if needed)
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.
Moving it back seems like it might be a bit nicer
83c35d9
to
d2476ed
Compare
Thanks for the review! I have 2 remaining questions,
|
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.
LGTM
I think that makes sense; from the point of view of calling code they're overloads, and the fact that one uses a
It's mildly suspicious, but I would say not worrying enough to hold up this patch. Feel free to file a follow-up issue about the FIXME so it's not forgotten. |
Fix for #109608.
Include methods that use explicit object in code complete suggestions.