Tuple support for DeclarationNameCompletionProvider (name suggestion)#22673
Conversation
…' in CSharpCompletionCommandHandlerTests.
|
Could I get some feedback please? |
| var typeInferenceService = document.GetLanguageService<ITypeInferenceService>(); | ||
|
|
||
| if (IsParameterDeclaration(token, semanticModel, position, cancellationToken, out var result) | ||
| if (IsTupleElementDeclaration(token, semanticModel, position, cancellationToken, out var result) |
There was a problem hiding this comment.
💭 Why is IsTupleElementDeclaration up here, but IsTupleExpressionDeclaration called much lower in the list?
There was a problem hiding this comment.
IsTupleElementDeclaration needs to come before IsParameterDeclaration. Otherwise constructs as
void M((System.Array $$ or (System.Array $$ are treated by IsParameterDeclaration and the completion list is empty. This is covered by TupleElementDefinition6 and TupleElementDefinition7.
There was a problem hiding this comment.
Otherwise constructs as
void M((System.Array $$[is] treated by IsParameterDeclaration
That itself seems like a bug. IsParameterDeclaration doesn't seem like it should be true here...
There was a problem hiding this comment.
IsParameterDeclaration doesn't seem like it should be true here
This is the syntax tree of void M((System.Array $$))
So the token is part of a TupleType that itself is a parameter . So @CyrusNajmabadi is right as this is a type declaration taking place in a parameter. This can be solved the way I did (check for tuple declarations before parameter declarations) or by special casing TupleType in IsParameterDeclaration. I'm fine with both ways.
|
|
||
| private static bool IsTupleExpressionDeclaration(SyntaxToken token, SemanticModel semanticModel, int position, CancellationToken cancellationToken, out NameDeclarationInfo result) | ||
| { | ||
| if (token.GetAncestor<TupleExpressionSyntax>() != null) |
There was a problem hiding this comment.
❓ Why is this GetAncestor? I'm concerned that in a nested expression scenario this could provide incorrect results. Can you describe the scenarios that IsTupleExpressionDeclaration applies is meant to handle?
There was a problem hiding this comment.
The TupleExpressionSyntax is a few steps above the token:
(System.Array array, System.Action $$
IdentifierNameSyntax IdentifierName Action
↳ MemberAccessExpressionSyntax SimpleMemberAccessExpression System.Action
↳ ArgumentSyntax Argument System.Action
↳ TupleExpressionSyntax TupleExpression (System.Array array, System.Action
The call IsFollowingTypeOrComma does some further checks of the relationship between the token and the node so nesting shouldn't be a problem.
… tuples. Renamed tests for clarity.
…ts and support for suggestion in the middle of a tuple.
|
@sharwell Is the IDE-team interested in this or should I close it? |
| switch (argumentSyntax.Expression?.Kind()) | ||
| { | ||
| case SyntaxKind.DeclarationExpression: | ||
| return (argumentSyntax.Expression as DeclarationExpressionSyntax).Type; |
There was a problem hiding this comment.
just do a normal cast, you did the 'case' check above.
| case SyntaxKind.DeclarationExpression: | ||
| return (argumentSyntax.Expression as DeclarationExpressionSyntax).Type; | ||
| default: | ||
| return argumentSyntax.Expression; |
There was a problem hiding this comment.
This seems odd... you asked for the type syntax, but you're returning just hte expression? I can envision why you want this. But this line def needs a comment explaining why this is ok.
…ent and added comments.
| { | ||
| switch (argumentSyntax.Expression?.Kind()) | ||
| { | ||
| case SyntaxKind.DeclarationExpression: |
|
We apologize, but we are closing this PR due to code drift. We regret letting this PR go so long without attention, but at this point the code base has changed too much for us to revisit older PRs. Going forward, we will manage PRs better under an SLA currently under draft in issue #26266 – we encourage you to add comments to that draft as you see fit. Although that SLA is still in draft form, we nevertheless want to move forward with the identification of older PRs to give contributors as much early notice as possible to review and update them. If you are interested in pursuing this PR, please reset it against the head of master and we will reopen it. Thank you! |
|
@jaredpar Please reopen this PR and assign a "buddy". It was me waiting for feedback not the other way around. |
| } | ||
|
|
||
| [WorkItem(22342, "https://github.com/dotnet/roslyn/issues/22342")] | ||
| [Fact(Skip = "Not yet supported"), Trait(Traits.Feature, Traits.Features.Completion)] |
There was a problem hiding this comment.
Please unskip the test, capture the current behavior and add a link to an issue for tracking the remaining gap.
| SyntaxToken token, SemanticModel semanticModel, int position, | ||
| CancellationToken cancellationToken, out NameDeclarationInfo result) | ||
| { | ||
| if (token.GetAncestor(node => node.IsKind(SyntaxKind.TupleExpression)) != null) |
There was a problem hiding this comment.
This ancestor check feels too loose. Imagine an invocation inside a tuple expression, and the caret is after the comma in the argument list of the invocation.
Can we just check the parent or grandparent instead?
There was a problem hiding this comment.
I tried to come up with a test to make this fail but I couldn't. Can you have a look in commit 3ebfe28? We somehow need to get a type declaration inside that call to make the completion fail. The method examines code like the following and I don't see how there can be a method invocation:
class Test
{
void Do()
{
(System.Array array, System.Action $$
}
}There was a problem hiding this comment.
See also my previous discussion with @sharwell #22673 (comment)
| } | ||
|
|
||
| private static bool IsTupleExpressionDeclaration( | ||
| SyntaxToken token, SemanticModel semanticModel, int position, |
There was a problem hiding this comment.
The position argument seems unused.
There was a problem hiding this comment.
There are a bunch of methods with about the same signature:
if (IsTupleElementDeclaration(token, semanticModel, position, cancellationToken, out var result)
|| IsParameterDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsTypeParameterDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsVariableDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsIncompleteMemberDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsFieldDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsMethodDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsPropertyDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsPossibleOutVariableDeclaration(token, semanticModel, position, typeInferenceService, cancellationToken, out result)
|| IsTupleExpressionDeclaration(token, semanticModel, position, cancellationToken, out result)
|| IsPossibleVariableOrLocalMethodDeclaration(token, semanticModel, position, cancellationToken, out result))I would like to keep the signature as is just for symmetry.
There was a problem hiding this comment.
Saw that. Fine to leave as-is :-)
| // We also assume that this name could be resolved to a type. | ||
| return argumentSyntax.Expression; | ||
| } | ||
| } |
There was a problem hiding this comment.
Not directly related to this PR: FYI I added a note to the test plan for recursive patterns to test declaration name completion. For instance: e is (System.Array $$.
#25935
| return default; | ||
| } | ||
|
|
||
| private static bool IsTupleElementDeclaration( |
There was a problem hiding this comment.
Nit: Consider renaming this method to IsTupleTypeElement and the following one IsTupleLiteralElement.
|
|
||
| [WorkItem(22342, "https://github.com/dotnet/roslyn/issues/22342")] | ||
| [Fact, Trait(Traits.Feature, Traits.Features.Completion)] | ||
| public async void TupleExpressionDeclaration1() |
There was a problem hiding this comment.
The test name is confusing. This is a tuple type, not a tuple expression.
I think the same applies for some other tests.
There was a problem hiding this comment.
use async Task for all tests
There was a problem hiding this comment.
BTW, the existing tests are already fixed if you merge latest master. Please remove async void from your tests too.
There was a problem hiding this comment.
@jcouv
You are right. The reason for this is that the code under test is parsed as a tuple expression.

I haven't figured out how to find better names for such the tests.
| return result.Type != null; | ||
| } | ||
|
|
||
| private static bool IsTupleExpressionDeclaration( |
There was a problem hiding this comment.
It would be good to add a comment to clarify that this is for handling incomplete code that parsed like a tuple literal.
But the purpose is not to help typing names in legit tuple literals (that's not possible since they don't have any TypeSyntax).
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (iteration 7).
| } | ||
|
|
||
| [WorkItem(22342, "https://github.com/dotnet/roslyn/issues/22342")] | ||
| [Fact(Skip = "Not yet supported"), Trait(Traits.Feature, Traits.Features.Completion)] |
There was a problem hiding this comment.
If we unskip the previous test, should we unskip this one too?
There was a problem hiding this comment.
Muscle memory is strong, my German friend ;-)
"ist"
|
@jinujoseph for approval to merge |
|
Approved to merge for 15.8.Preview3 |

(note from jcouv: Merge, do not squash this PR, as it includes merge commits from master)
Customer scenario
Suggested names support for tuples.
Bugs this fixes:
#22342 (Kind of)
Workarounds, if any
No. Feature improvement.
Risk
Name suggestion might be triggered in inappropriate circumstances.
Performance impact
Editor performance and memory critical.
Is this a regression from a previous update?
No. Feature improvement.
Root cause analysis:
Tuple support was missing.
How was the bug found?
Found by investigating #22342. #22342 reports a (very) special case but more general cases were missing too.
Test documentation updated?
No
Remarks
It seems DeclarationNameCompletionProvider does not handle tuples at all. In the following example none of the occurrence of
name,actionandaccessViolationExceptionis suggested):#22342 suggested that there is support for tuples but there isn't (Just typing
(Arraysuggest array because it triggersIsPossibleVariableOrLocalMethodDeclaration.).This PR adds support for the cases above (except
(var array, var action)).TupleElementDefinition1toTupleElementDefinition7test the new tuple support. The remaining two tests fail becauseTupleElementTypeInferenceI couldn't get the typeInference to work andTupleElementInGenericTypeArgumentThe expression tree generated by the code of Suggested names not provided for tuple elements in generic type arguments #22342System.Func<(System.Action $$has nothing to do with tuples at all (combination of LessThan and Parenthesized expression). It's questionable if this really should be supported.I hardly understand what my call to
IsLastTokenOfTypedoes. It gives the desired result but reviewers should take a close look whether that call is appropriate.