Skip to content
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

Completion: Suggest types after keywords indicating a local function #54493

Merged

Conversation

MaStr11
Copy link
Contributor

@MaStr11 MaStr11 commented Jun 30, 2021

Fixes #53585

cc @Youssef1313

@MaStr11 MaStr11 requested a review from a team as a code owner June 30, 2021 12:51
Comment on lines 19 to 22
SyntaxKind.ExternKeyword,
SyntaxKind.StaticKeyword,
SyntaxKind.AsyncKeyword,
SyntaxKind.UnsafeKeyword,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This list is derived from all calls to IsLocalFunctionDeclarationContext (ExternKeywordRecommender, StaticKeywordRecommender, AsyncKeywordRecommender and UnsafeKeywordRecommender). The spec only mentions async and unsafe, but dates back to C# 7.0.

@@ -78,7 +78,7 @@ private ImmutableArray<ISymbol> GetSymbolsForCurrentContext()
// is a type-only context, we'll show all symbols anyway.
return GetSymbolsForExpressionOrStatementContext();
}
else if (_context.IsTypeContext || _context.IsNamespaceContext)
else if (_context.IsTypeContext || _context.IsNamespaceContext || _context.IsLocalFunctionDeclarationContext)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CyrusNajmabadi This is probably naive or just plain wrong. Do you have any objections? If not, I will add some more tests.

Copy link
Member

@Youssef1313 Youssef1313 Jun 30, 2021

Choose a reason for hiding this comment

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

I'm not sure if IsTypeContext should be true here instead of introducing the new property. Also consider revisiting other callers to IsTypeContext and check what the desired behavior is for these callers.

For example, generate type codefix is (most likely) also affected by IsTypeContext returning false.

if (!semanticFacts.IsTypeContext(semanticModel, NameOrMemberAccessExpression.SpanStart, cancellationToken) &&

class C
{
    Test NotLocal() { return null; } // Generate class 'Test' is offered here
    
    void M()
    {
        static Test Local(){ return null; } // But not offered here.
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I gave this a try in 842a1f6 and removed the new property in eb1b5be. This results in one test failure, discussed below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tracked down every reference of IsTypeContext (at least I tried) and added tests for each occurrence.

}
}
";
await VerifyItemExistsAsync(markup, "String");
Copy link
Member

Choose a reason for hiding this comment

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

Is string offered too? It has a separate keyword recommender IIRC (there are other keywords too).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It wasn't offered. I fixed this and added tests.

@jinujoseph jinujoseph added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jul 5, 2021
@MaStr11
Copy link
Contributor Author

MaStr11 commented Jul 19, 2021

@CyrusNajmabadi This simple approach expands syntaxTree.IsTypeContext, to also be true in syntaxTree.IsLocalFunctionDeclarationContext. The only test that fails with this addition is the delegate keyword recommender (test failure: delegate is suggested after static):

public async Task TestAfterStatic()
{
await VerifyAbsenceAsync(SourceCodeKind.Regular, @"static $$");

The root cause is here in ValidTypeContext:

protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
return
context.IsGlobalStatementContext ||
ValidTypeContext(context) ||
IsAfterAsyncKeywordInExpressionContext(context, cancellationToken) ||
context.IsTypeDeclarationContext(
validModifiers: s_validModifiers,
validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations,
canBePartial: false,
cancellationToken: cancellationToken);
static bool ValidTypeContext(CSharpSyntaxContext context)
=> (context.IsNonAttributeExpressionContext || context.IsTypeContext)
&& !context.IsConstantExpressionContext
&& !context.LeftToken.IsTopLevelOfUsingAliasDirective();
}

I'm not too confident about the delegate keyword, but to me, it feels like it should not be suggested in a type context. For instance, it is suggested in method parameters (void M($$), which I think is wrong. @CyrusNajmabadi It probably should read context.IsTypeDeclarationContext in line 44?

@CyrusNajmabadi What do you think about the approach of expanding syntaxTree.IsTypeContext? What should we do about the DelegateKeywordRecommender.

@CyrusNajmabadi
Copy link
Member

delegate is now legal in a type-context due to function pointers (which look like delegate*<int, void>). So i would update thigns accordingly based on that. If you still have any remaining issues/questions, lmk at that point. Thanks!

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jul 20, 2021

@CyrusNajmabadi Ready for review

@@ -288,7 +288,7 @@ public async Task TestNotAfterSealed()
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterStatic()
{
await VerifyAbsenceAsync(SourceCodeKind.Regular, @"static $$");
await VerifyKeywordAsync(SourceCodeKind.Regular, @"static $$");
await VerifyKeywordAsync(SourceCodeKind.Script, @"static $$");
Copy link
Member

Choose a reason for hiding this comment

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

can we have a test for this after static inside a method as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in b581ce2 (with a fix in 8439d03)

public async Task TestAfterStaticLocalFunction()
{
await VerifyKeywordAsync(AddInsideMethod(@"
static $$"));
Copy link
Member

Choose a reason for hiding this comment

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

so, you had all the tests after 'static' in a method, can you do the same for after 'async'? as well? or make all the tests theories and test static async async static and static async. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used a ClassData theory added in b46098a and applied in f686b67.

Copy link
Member

@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.

LGTM with requested test additions.

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jul 21, 2021

@CyrusNajmabadi Feedback is addressed and tests are green (except one unrelated integration test failure). I added the new file TheoryDataKeywordsIndicatingLocalFunction.cs for the theory data ([ClassData(typeof(...)]) for reuse in all the recommender tests. I hope that's fine (using [MemberData] and a property on the tests base class would be an alternative).

internal class TheoryDataKeywordsIndicatingLocalFunction : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { "extern" };
yield return new object[] { "static extern" };

…xtensions/ContextQuery/SyntaxTreeExtensions.cs
@CyrusNajmabadi
Copy link
Member

Thanks!

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@CyrusNajmabadi
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@CyrusNajmabadi CyrusNajmabadi merged commit e36e194 into dotnet:main Jul 28, 2021
@ghost ghost added this to the Next milestone Jul 28, 2021
@MaStr11 MaStr11 deleted the TypeCompletionAfterLocalFunctionKeywords branch July 29, 2021 07:20
@jaredpar jaredpar mentioned this pull request Aug 17, 2021
@dibarbet dibarbet modified the milestones: Next, 17.0.P4 Aug 31, 2021
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.

Completion doesn't offer return types for static local functions
5 participants