MakeMethodAsync: fix iterator methods#31846
Conversation
|
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 |
src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs
Outdated
Show resolved
Hide resolved
...atures/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
| 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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Or alternatively, feel free to tell me to shut up at this point 😄
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
can you not use nameof here?
There was a problem hiding this comment.
That type is not referenced (it has not shipped yet).
| TypeSyntax MakeGenericType(string type, ITypeSymbol typeArgumentFrom) | ||
| { | ||
| return SyntaxFactory.GenericName( | ||
| SyntaxFactory.Identifier(type), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
In that case you will get an error and a suggestion to import the namespace.
I'm ok to fully qualify though.
There was a problem hiding this comment.
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() }))); |
There was a problem hiding this comment.
Use .SingletonSeparatedList.
...atures/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
...atures/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
...atures/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
...atures/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
| if (!keepVoid) | ||
| { | ||
| newReturnType = knownTypes.TaskType.GenerateTypeSyntax(); | ||
| newReturnType = knownTypes._taskType.GenerateTypeSyntax(); |
There was a problem hiding this comment.
note, if these are public readonlys, they don't need to be named _...
There was a problem hiding this comment.
That seems to be the configured naming convention
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
why is KnownTypes defined in two places?
|
@CyrusNajmabadi Please take another look when you have some time. Thanks |
| public readonly INamedTypeSymbol _iEnumeratorOfTType; | ||
|
|
||
| public readonly INamedTypeSymbol _iAsyncEnumerableOfTType; | ||
| public readonly INamedTypeSymbol _iAsyncEnumeratorOfTType; |
There was a problem hiding this comment.
technically these last 5 types should have the Opt suffix (matching _valueTaskOfTTypeOpt).
|
@dotnet/roslyn-ide for review. Thanks |
1 similar comment
|
@dotnet/roslyn-ide for review. Thanks |
| { | ||
| internal readonly struct KnownTypes | ||
| { | ||
| public readonly INamedTypeSymbol _taskType; |
There was a problem hiding this comment.
Nit: Consider using auto-props instead.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
I agree. I think I prefer simple field for now.
Thanks
|
@jinujoseph Please approve for preview2. Thanks |
If you have a method returning
IEnumerable<>, then add anawait, 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