Skip to content

MakeMethodAsync: fix iterator methods#31846

Merged
jcouv merged 7 commits intodotnet:masterfrom
jcouv:make-async
Dec 21, 2018
Merged

MakeMethodAsync: fix iterator methods#31846
jcouv merged 7 commits intodotnet:masterfrom
jcouv:make-async

Conversation

@jcouv
Copy link
Copy Markdown
Member

@jcouv jcouv commented Dec 16, 2018

If you have a method returning IEnumerable<>, then add an await, the fixer helps you fix the declaration to make an async-iterator.
If you have an async-iterator method, the remove all awaits, the fixer helps you fix the declaration to make an iterator.

Fixes #31841

FYI @Neme12 @CyrusNajmabadi

@jcouv jcouv added this to the 16.0.P2 milestone Dec 16, 2018
@jcouv jcouv self-assigned this Dec 16, 2018
@jcouv jcouv requested a review from a team as a code owner December 16, 2018 22:24
@Neme12
Copy link
Copy Markdown
Contributor

Neme12 commented Dec 16, 2018

Note: you might also want to take a look at fixing "Make method sync". In case you have an async iterator method but there are no awaits inside, there should be a code fix offered for the compiler warning to remove async and change the return type from IAsyncEnumerable to IEnumerable.

bool ContainsYield(Location l)
=> l.FindNode(default).DescendantNodes().Any(n => IsYield(n));
bool ContainsYield(SyntaxNode node)
=> node.DescendantNodes(n => n == node || !n.IsKind(SyntaxKind.LocalFunctionStatement)).Any(n => IsYield(n));
Copy link
Copy Markdown
Contributor

@Neme12 Neme12 Dec 16, 2018

Choose a reason for hiding this comment

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

Very tiny nit: consider just using .IsReturnableConstruct instead of IsKind. I understand that will make no difference on all correct code because lambdas cannot have yields but it seems appropriate to stop the search at any kind of nested function, and that this method shouldn't really have to think about which of those can contain yields and which cannot.

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.

Or alternatively, feel free to tell me to shut up at this point 😄

Copy link
Copy Markdown
Contributor

@Neme12 Neme12 Dec 16, 2018

Choose a reason for hiding this comment

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

Good catch with the n == node part. I would have missed that. Do you have a test where the iterator method is a local function, to make sure this check is covered?

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.

Yes, I caught that case with a test. I'd missed it initially too ;-)

var returnType = methodSymbol.ReturnType;
if (IsIEnumerable(returnType, knownTypes) && IsIterator(methodSymbol))
{
newReturnType = MakeGenericType("IAsyncEnumerable", methodSymbol.ReturnType);
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 not use nameof here?

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.

That type is not referenced (it has not shipped yet).

TypeSyntax MakeGenericType(string type, ITypeSymbol typeArgumentFrom)
{
return SyntaxFactory.GenericName(
SyntaxFactory.Identifier(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.

this seems wrong. how will this work if they don't have an import for the namespace where these types live? We should be fully qualifying these.

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.

In that case you will get an error and a suggestion to import the namespace.
I'm ok to fully qualify though.

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.

It's easy to do that too just using SyntaxGenerator.TypeExpression. Or ITypeSymbol.GenerateTypeSyntax. They will even put the right annotations on things so things will simplify properly :)

{
return SyntaxFactory.GenericName(
SyntaxFactory.Identifier(type),
SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(new[] { typeArgumentFrom.GetTypeArguments()[0].GenerateTypeSyntax() })));
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.

Use .SingletonSeparatedList.

if (!keepVoid)
{
newReturnType = knownTypes.TaskType.GenerateTypeSyntax();
newReturnType = knownTypes._taskType.GenerateTypeSyntax();
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.

note, if these are public readonlys, they don't need to be named _...

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.

That seems to be the configured naming convention

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.

sounds liek a mistake. IDE has lots of of public readonly fields that are not underscored. underscores are intended to only be for private members. @sharwell do you know what's up here?

}
}

protected struct KnownTypes
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.

why is KnownTypes defined in two places?

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Dec 18, 2018

@CyrusNajmabadi Please take another look when you have some time. Thanks

public readonly INamedTypeSymbol _iEnumeratorOfTType;

public readonly INamedTypeSymbol _iAsyncEnumerableOfTType;
public readonly INamedTypeSymbol _iAsyncEnumeratorOfTType;
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.

technically these last 5 types should have the Opt suffix (matching _valueTaskOfTTypeOpt).

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Dec 18, 2018

@dotnet/roslyn-ide for review. Thanks

1 similar comment
@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Dec 19, 2018

@dotnet/roslyn-ide for review. Thanks

{
internal readonly struct KnownTypes
{
public readonly INamedTypeSymbol _taskType;
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.

Nit: Consider using auto-props instead.

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.

in general, IDE is totally fine with readonly-fields for internal simple-data structs. The props don't really buy anything since we have no need to change the impl over time. or, in other words, if we did need to change the impl, we own all the consumers, so we can just change them. so, the readonly field is just the simplest way of saying "this is just raw data, and nothing more".

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.

I agree. I think I prefer simple field for now.
Thanks

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Dec 20, 2018

@jinujoseph Please approve for preview2. Thanks

@jcouv jcouv merged commit 3bb73cf into dotnet:master Dec 21, 2018
@jcouv jcouv deleted the make-async branch December 21, 2018 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Make method async" in iterator method should change return type to IAsyncEnumerable instead of Task

5 participants