Skip to content

Suggest parameter names from matching overloads#52801

Merged
CyrusNajmabadi merged 13 commits intodotnet:mainfrom
Youssef1313:intellisense-completion-overloads
Feb 6, 2022
Merged

Suggest parameter names from matching overloads#52801
CyrusNajmabadi merged 13 commits intodotnet:mainfrom
Youssef1313:intellisense-completion-overloads

Conversation

@Youssef1313
Copy link
Copy Markdown
Member

Fixes #52534

@Youssef1313 Youssef1313 requested a review from a team as a code owner April 21, 2021 15:06
@ghost ghost added the Area-IDE label Apr 21, 2021
Comment on lines +306 to +308
foreach (var sibling in siblingMethods)
{
foreach (var siblingParameter in sibling.ParameterList.Parameters)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharwell Any performance concerns?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. There are other completion scenarios that are more expensive without problems.

Maybe try running this in a class with a large number of methods all with different names, just to make sure the outer loop isn't too slow in huge types?

@jinujoseph jinujoseph added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Apr 21, 2021
Comment on lines +251 to +253
var partialSemanticModel = await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (partialSemanticModel is not null)
AddNamesFromExistingOverloads(context, partialSemanticModel, result, cancellationToken);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caller already has a semantic model, but it's not a partial semantic model.

var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);

I used partial semantic model per your suggestion @sharwell.

@Youssef1313 Youssef1313 requested a review from sharwell April 27, 2021 06:13
@Youssef1313

This comment has been minimized.

@Youssef1313 Youssef1313 marked this pull request as draft April 28, 2021 08:45
@Youssef1313 Youssef1313 marked this pull request as ready for review April 28, 2021 09:26
@Youssef1313 Youssef1313 requested a review from CyrusNajmabadi May 3, 2021 06:46
@Youssef1313
Copy link
Copy Markdown
Member Author

@sharwell @CyrusNajmabadi Anything left here? Thanks!

Copy link
Copy Markdown
Member

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM, but would be good to get a tick from @CyrusNajmabadi or @sharwell for any existing perf concerns.

In particular whilst there might be de-duping code in completion itself, I don't know if this is something we want to rely on. Duplicates in overloads seem like they would be very common, and it would be very simple to filter them out here (add them to the hashset this is already checking).

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

I can look tomorrow

if (parameterSyntax is not { Type: { } parameterType, Parent: { Parent: BaseMethodDeclarationSyntax baseMethod } })
return;

var overloads = GetOverloads(baseMethod, namedType);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my preference is to swap these parameters so we always go from 'wider to narrow' scope for them.

if (!overloads.Any())
return;

var methodParameterType = semanticModel.GetTypeInfo(parameterType, cancellationToken).Type;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider doing this first before botherin gto collect overloads.


var overloads = GetOverloads(baseMethod, namedType);

if (!overloads.Any())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will causde double enumeration of an enumerable.

@Youssef1313
Copy link
Copy Markdown
Member Author

I fixed the conflicts. If CI failed, I will get to that in a week after finishing my exams.

@Youssef1313
Copy link
Copy Markdown
Member Author

@CyrusNajmabadi This should be ready for another look :)

}
";
await VerifyItemExistsAsync(markup, "myTok", glyph: (int)Glyph.Parameter);
await VerifyItemExistsAsync(markup, "cancellationToken", glyph: (int)Glyph.Parameter);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add an assert about what is selected?

Copy link
Copy Markdown
Member Author

@Youssef1313 Youssef1313 Dec 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi What's the API for doing so? Or does it need an integration test?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you'll need to add a test to CompletionCommandHandlerTests

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the test.

{
return baseMethod switch
{
MethodDeclarationSyntax method => namedType.GetMembers(method.Identifier.ValueText).OfType<IMethodSymbol>().ToImmutableArray(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to collect from teh base type as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong opinion. I think we can keep it simple for now instead of walking through base types.

Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving. but if you want to add another test, i would like that.

@Youssef1313
Copy link
Copy Markdown
Member Author

@CyrusNajmabadi Sorry for the delay. I added the test you requested.


var parameterSyntax = context.LeftToken.GetAncestor(n => n.IsKind(SyntaxKind.Parameter)) as ParameterSyntax;
if (parameterSyntax is not { Type: { } parameterType, Parent: { Parent: BaseMethodDeclarationSyntax baseMethod } })
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you check that the left token is hte last token of the parameter type. i'm actually worried about you offering these names inside the parameter type. Can you add tests that shows this doesn't happen (for example, in the type arg of a generic type).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Likely already handled in GetDeclarationInfoAsync. I'm not sure about the exact test case you're referring to. Something like this?

void M<T>($$) { }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. more like: void M(List<$$> the left token will be inside a parameter here.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

overall, i like this. but i do have the concern about this showing up not inside more than just the parameter name.

return;

var parameterSyntax = context.LeftToken.GetAncestor(n => n.IsKind(SyntaxKind.Parameter)) as ParameterSyntax;
if (parameterSyntax is not { Type: { } parameterType, Parent: { Parent: BaseMethodDeclarationSyntax baseMethod } })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (parameterSyntax is not { Type: { } parameterType, Parent: { Parent: BaseMethodDeclarationSyntax baseMethod } })
if (parameterSyntax is not { Type: { } parameterType, Parent.Parent: BaseMethodDeclarationSyntax baseMethod })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we don't want this in the optional initializer value of a parameter either.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, we don't want this in the optional initializer value of a parameter either.

I'm not sure what you mean.

// I think this is not handled by declaration name completion at all?
public void M(string s = $$)
{
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically, i'm saying that this test doesn't guarantee that (even if some other check does). So it would be good to have a test here for those cases, just to show there's no issue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Yeah sure adding the test is good for coverage anyway even if already handled. I was trying to make sure I'm properly understanding the scenario you're referring to.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup. that's the scenario. the core issue i have with this code is that all it does is check if it's contained within a parameter.

Now, it sounds like there's other code that ensures that we're typing the parameter name. If so, that's great. I just want tests to ensure that works properly. thanks!

Youssef1313 and others added 2 commits February 6, 2022 09:45
…clarationNameCompletionProvider.cs

Co-authored-by: CyrusNajmabadi <cyrus.najmabadi@gmail.com>
@Youssef1313
Copy link
Copy Markdown
Member Author

@CyrusNajmabadi Anything left?

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

Thanks!

@CyrusNajmabadi CyrusNajmabadi merged commit be221d5 into dotnet:main Feb 6, 2022
@ghost ghost added this to the Next milestone Feb 6, 2022
@Youssef1313 Youssef1313 deleted the intellisense-completion-overloads branch February 6, 2022 17:38
@RikkiGibson RikkiGibson modified the milestones: Next, 17.2.P2 Mar 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggest names for matching parameters from overloads

7 participants