This is an archive of the discontinued LLVM Phabricator instance.

[CodeComplete] Improve FunctionCanBeCall
AbandonedPublic

Authored by zyounan on Jul 15 2023, 6:38 AM.

Details

Summary

This tries to avoid extra calculations in D137040.

In that patch the extra completion strings were dropped at the client
site, after function parameters became available, to implement the
heuristic. However, we can make the process a bit more terse: In the
context where a call isn't required, we could teach the Sema to emit
the completion string without additional function parameters.

In addition, this patch adds support for function templates to
FunctionCanBeCall as well. In such context, we'd emit extra template
argument placeholders that help avoiding unresolved overloads error.

Diff Detail

Event Timeline

zyounan created this revision.Jul 15 2023, 6:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 15 2023, 6:38 AM
zyounan added a comment.EditedJul 15 2023, 7:08 AM

Changing the code completion string to RK_Pattern makes the completion clearer, in a way that showing candidates with signatures in the completion items if FunctionCanBeCall while a mere name otherwise.

(Comparing to that we always show signatures even if no parameters are produced when hit the item.)

->


P.S. Despite the fact that I changed many associated tests, I still hope people don't rely on the former behavior so that this patch could be marked as NFC. :D

I discovered that patch when trying to figure out why clangd doesn't complete function parameters for members in global scope while it produces parameters when I'm inside a call expression. Regard to the heuristic itself, it's currently a bit inconvenient that I have to switch headers and sources back and forth and do copy-pastes when I'm writing member function definitions outside of a class. I think we could be more lenient here e.g., don't set the flag on if we're in the global scope. What do you guys think? (I'm working on another follow-up patch to address this, although it is not strightforward to tell which scope the building Declarator is in inside Sema. )

zyounan updated this revision to Diff 540687.Jul 15 2023, 7:48 AM
This comment was removed by zyounan.
zyounan published this revision for review.Jul 15 2023, 5:42 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJul 15 2023, 5:42 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

I'm not sure switching to RK_Pattern is ideal, but I think there are a couple of (easy?) alternatives that achieve the same.

Regard to the heuristic itself, it's currently a bit inconvenient that I have to switch headers and sources back and forth and do copy-pastes when I'm writing member function definitions outside of a class.

Yeah, it would be nice to have completions that provide the declaration syntax.
There's currently support for adding declarations of method overrides to derived classes: see AddOverrideResults in SemaCodeComplete, this should be kind-of similar.
These *do* use RK_Pattern, and I think it's appropriate there. (Though it does cause some ranking problems...)

clang-tools-extra/clangd/CodeComplete.cpp
441

I guess an alternative to this patch would be to clear S.Signature here too?

clang/lib/Sema/SemaCodeComplete.cpp
1422

the problem (from clangd's perspective) with emitting this as a pattern is we don't know which decl this is, and so we lose:

  • ranking information (score popular decls higher)
  • semantic information (this is a function and should be marked in LSP as such)

Is it possible to change the behavior of Result::CreateCodeCompletionString() instead, based on Result::FunctionCanBeCall? Then we get to keep RK_Declaration and get the right string

zyounan updated this revision to Diff 543277.Jul 23 2023, 7:20 AM

Don't change the CCR. Handle these in CreateCodeCompletionString. Emit template arguments if possible.

zyounan retitled this revision from [CodeComplete] Don't emit parameters when not FunctionCanBeCall to [CodeComplete] Improve FunctionCanBeCall.Jul 23 2023, 7:24 AM
zyounan edited the summary of this revision. (Show Details)
zyounan edited the summary of this revision. (Show Details)Jul 23 2023, 7:24 AM

Thanks for the insightful suggestions!
(Apologies for my late update. Just had a really busy week.)

As suggested, I left the CCR intact and handled these functions in CodeCompletionResult::createCodeCompletionStringForDecl. I think this preserves the Declaration, right? (While I think we could get the associated Decl if using RK_Pattern, however the current approach looks more terse to me.)

I also noticed that the previous implementation did not consider function templates. For example,

struct S {
  template <typename T>
  void foo(T);

  void bar();
};

&S::bar^ // getting `S::bar`
&S::foo^ // getting `S::foo(T)`!

This brings a discrepancy, which I have also fixed in this patch. I hope this doesn't cause too much inconvenience for the review :)

zyounan added inline comments.Jul 25 2023, 1:53 AM
clang/lib/Sema/SemaCodeComplete.cpp
1407

The current heuristic results in the following:

struct Base {
  void foo(int);
};
struct Derived : Base {
  void foo(float);
};

int main() {
  Derived d;
  d.Base::^    // FunctionCanBeCall for `foo` is false.
}

In situations where it is intended to invoke base member functions hidden by subclasses, we shouldn't remove parentheses, IMO.

zyounan added inline comments.Jul 29 2023, 9:32 PM
clang/lib/Sema/SemaCodeComplete.cpp
1407

D156605 to address this.

I'm not sure how I feel about dropping the parameters from the signature in the CanBeCall = false case.

I can see arguments in both directions:

  • On the one hand, dropping the parameters makes the text that is inserted more consistent with the text that is shown for the proposal.
  • On the other hand, imagine you're typing in a CanBeCall = false context (e.g. &ClassName::Prefix^), and there are several matching method names that begin with Prefix that you are trying to choose from. Seeing their signatures might be a useful piece of additional context to help you choose the right one (e.g. maybe you're looking for one with a particular parameter type).

On the whole, I think I lean towards the second point being more important, and therefore towards keeping the signatures.

zyounan added a comment.EditedAug 1 2023, 4:37 AM

(e.g. maybe you're looking for one with a particular parameter type).

I understand the second point that it'd be nice to offer the user a chance to see the parameters in order to help decide if the function is appropriate -- although in the context where CanBeCall=false, parameters don't disambiguate against the overloads, so we'd end up with the same function name after selecting the candidate. (An explicit cast may be required to perform overload resolution if necessary, but that should occur before the completion point &ClassName::Prefix^.)

OTOH, we don't present parameters for overloads in the candidates (using the clangd without this patch):

(At least for VSCode, I'm not sure if others behave the same.)

So, I don't think it is that important to retain the Signature. Any thoughts?

in the context where CanBeCall=false, parameters don't disambiguate against the overloads, so we'd end up with the same function name after selecting the candidate

I was thinking of functions with different names (with a common prefix) and different signatures, where the signature could be a useful piece of information in addition to the name to help you choose the right one.

For example, consider:

struct Thread {
  void sleep_until(time_point);
  void sleep_for(time_duration);
};

&Thread::sleep^

Maybe the distinction between the functions is less obvious from their names (until vs. for), but more obvious from the type of argument they take.

An explicit cast may be required to perform overload resolution if necessary, but that should occur before the completion point &ClassName::Prefix^.

(That would be an interesting future enhancement to consider for the overload set case.)

OTOH, we don't present parameters for overloads in the candidates (using the clangd without this patch):

(At least for VSCode, I'm not sure if others behave the same.)

Note, we have a flag that affects this, --completion-style=detailed.

zyounan planned changes to this revision.Aug 3 2023, 12:41 AM

I was thinking of functions with different names (with a common prefix) and different signatures, where the signature could be a useful piece of information in addition to the name to help you choose the right one.

Ah, that makes sense! Honestly, I've never thought the signature could assist such a scenario.

Note, we have a flag that affects this, --completion-style=detailed.

Thanks. Just learned this flag.

Apart from the Signature, I still have two improvements for CanBeCall. I think it's better to close this review and merge these two changes into one patch.

zyounan abandoned this revision.Aug 6 2023, 5:23 AM

Closing this in favor of D156605.