diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 4a8ff5348bebf..8fb295e4e7ef2 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -2482,10 +2482,16 @@ protected bool IsGenericTaskReturningAsyncMethod()
return symbol?.Kind == SymbolKind.Method && ((MethodSymbol)symbol).IsGenericTaskReturningAsync(this.Compilation);
}
- protected bool IsIAsyncEnumerableReturningAsyncMethod()
+ protected bool IsIAsyncEnumerableOrIAsyncEnumeratorReturningAsyncMethod()
{
var symbol = this.ContainingMemberOrLambda;
- return symbol?.Kind == SymbolKind.Method && ((MethodSymbol)symbol).IsIAsyncEnumerableReturningAsync(this.Compilation);
+ if (symbol?.Kind == SymbolKind.Method)
+ {
+ var method = (MethodSymbol)symbol;
+ return method.IsIAsyncEnumerableReturningAsync(this.Compilation) ||
+ method.IsIAsyncEnumeratorReturningAsync(this.Compilation);
+ }
+ return false;
}
protected virtual TypeSymbol GetCurrentReturnType(out RefKind refKind)
@@ -2547,7 +2553,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di
diagnostics.Add(ErrorCode.ERR_MustNotHaveRefReturn, syntax.ReturnKeyword.GetLocation());
hasErrors = true;
}
- else if (IsIAsyncEnumerableReturningAsyncMethod())
+ else if (IsIAsyncEnumerableOrIAsyncEnumeratorReturningAsyncMethod())
{
diagnostics.Add(ErrorCode.ERR_ReturnInIterator, syntax.ReturnKeyword.GetLocation());
hasErrors = true;
diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs
index 6a76ef8c943d8..2ab1dd8218a19 100644
--- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs
@@ -155,7 +155,7 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
}
else if (!returnType.IsErrorType())
{
- Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType);
+ Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType);
}
elementType = CreateErrorType();
}
@@ -184,7 +184,8 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C
{
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType)
{
- switch (returnType.OriginalDefinition.SpecialType)
+ TypeSymbol originalDefinition = returnType.OriginalDefinition;
+ switch (originalDefinition.SpecialType)
{
case SpecialType.System_Collections_IEnumerable:
case SpecialType.System_Collections_IEnumerator:
@@ -200,7 +201,8 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}
- if (returnType.OriginalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T))
+ if (originalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T) ||
+ originalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T))
{
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index db7454443320b..8813a3867fdf6 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -1024,7 +1024,7 @@ internal static string ERR_BadAsyncMethodBuilderTaskProperty {
}
///
- /// Looks up a localized string similar to The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T>.
+ /// Looks up a localized string similar to The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>.
///
internal static string ERR_BadAsyncReturn {
get {
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index f1c2a94a0ec54..ddd5e292f2391 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -3630,7 +3630,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi
Since '{0}' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
- The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T>
+ The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>
Cannot return an expression of type 'void'
diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs
index 2c300afb05e72..d65901f2909e6 100644
--- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs
@@ -18,6 +18,10 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter
private FieldSymbol _promiseOfValueOrEndField; // this struct implements the IValueTaskSource logic
private FieldSymbol _currentField; // stores the current/yielded value
+ // true if the iterator implements IAsyncEnumerable,
+ // false if it implements IAsyncEnumerator
+ private readonly bool _isEnumerable;
+
internal AsyncIteratorRewriter(
BoundStatement body,
MethodSymbol method,
@@ -29,14 +33,21 @@ internal AsyncIteratorRewriter(
: base(body, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics)
{
Debug.Assert(method.IteratorElementType != null);
+
+ _isEnumerable = method.IsIAsyncEnumerableReturningAsync(method.DeclaringCompilation);
}
protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag)
{
base.VerifyPresenceOfRequiredAPIs(bag);
- EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag);
+
+ if (_isEnumerable)
+ {
+ EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag);
+ }
EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync, bag);
EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__get_Current, bag);
+
EnsureWellKnownMember(WellKnownMember.System_IAsyncDisposable__DisposeAsync, bag);
EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctor, bag);
@@ -62,8 +73,11 @@ protected override void GenerateMethodImplementations()
// IAsyncStateMachine methods and constructor
base.GenerateMethodImplementations();
- // IAsyncEnumerable
- GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator();
+ if (_isEnumerable)
+ {
+ // IAsyncEnumerable
+ GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator();
+ }
// IAsyncEnumerator
GenerateIAsyncEnumeratorImplementation_MoveNextAsync();
@@ -81,6 +95,9 @@ protected override void GenerateMethodImplementations()
GenerateIAsyncDisposable_DisposeAsync();
}
+ protected override bool PreserveInitialParameterValuesAndThreadId
+ => _isEnumerable;
+
protected override void GenerateControlFields()
{
// the fields are initialized from entry-point method (which replaces the async-iterator method), so they need to be public
@@ -153,8 +170,8 @@ protected override void GenerateConstructor()
protected override void InitializeStateMachine(ArrayBuilder bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal)
{
- // var stateMachineLocal = new {StateMachineType}(FinishedStateMachine)
- int initialState = StateMachineStates.FinishedStateMachine;
+ // var stateMachineLocal = new {StateMachineType}({initialState})
+ int initialState = _isEnumerable ? StateMachineStates.FinishedStateMachine : StateMachineStates.NotStartedStateMachine;
bodyBuilder.Add(
F.Assignment(
F.Local(stateMachineLocal),
diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs
index cfef83c811223..af41d21af7650 100644
--- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs
@@ -106,9 +106,8 @@ private Symbol EnsureWellKnownMember(WellKnownMember member, DiagnosticBag bag)
return Binder.GetWellKnownTypeMember(F.Compilation, member, bag, body.Syntax.Location);
}
- // Should only be true for async-enumerables, not async-enumerators. Tracked by https://github.com/dotnet/roslyn/issues/31057
protected override bool PreserveInitialParameterValuesAndThreadId
- => method.IsIterator;
+ => false;
protected override void GenerateControlFields()
{
diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs
index b6cf218295b5a..71e3b99196605 100644
--- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs
@@ -31,8 +31,12 @@ public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompila
var elementType = TypeMap.SubstituteType(asyncMethod.IteratorElementType).TypeSymbol;
this.IteratorElementType = elementType;
- // IAsyncEnumerable
- interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType));
+ bool isEnumerable = asyncMethod.IsIAsyncEnumerableReturningAsync(compilation);
+ if (isEnumerable)
+ {
+ // IAsyncEnumerable
+ interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType));
+ }
// IAsyncEnumerator
interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T).Construct(elementType));
diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs
index 506f064dbe1b5..b2d106aa98d46 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs
@@ -314,6 +314,15 @@ public static bool IsIAsyncEnumerableReturningAsync(this MethodSymbol method, CS
&& method.ReturnType.TypeSymbol.IsIAsyncEnumerableType(compilation);
}
+ ///
+ /// Returns whether this method is async and returns an IAsyncEnumerator`1.
+ ///
+ public static bool IsIAsyncEnumeratorReturningAsync(this MethodSymbol method, CSharpCompilation compilation)
+ {
+ return method.IsAsync
+ && method.ReturnType.TypeSymbol.IsIAsyncEnumeratorType(compilation);
+ }
+
internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method)
{
method = method.PartialDefinitionPart ?? method;
diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
index 7aea1fa8835a7..540922ae00d44 100644
--- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
@@ -1450,6 +1450,17 @@ internal static bool IsIAsyncEnumerableType(this TypeSymbol type, CSharpCompilat
return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T);
}
+ internal static bool IsIAsyncEnumeratorType(this TypeSymbol type, CSharpCompilation compilation)
+ {
+ var namedType = type as NamedTypeSymbol;
+ if ((object)namedType == null || namedType.Arity != 1)
+ {
+ return false;
+ }
+
+ return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T);
+ }
+
///
/// Returns true if the type is generic or non-generic custom task-like type due to the
/// [AsyncMethodBuilder(typeof(B))] attribute. It returns the "B".
@@ -1694,10 +1705,12 @@ private static bool IsWellKnownInteropServicesTopLevelType(this ITypeSymbol type
public static bool IsBadAsyncReturn(this TypeSymbol returnType, CSharpCompilation declaringCompilation)
{
// Note: we're passing the return type explicitly (rather than using `method.ReturnType`) to avoid cycles
- return returnType.SpecialType != SpecialType.System_Void &&
+ return !returnType.IsErrorType() &&
+ returnType.SpecialType != SpecialType.System_Void &&
!returnType.IsNonGenericTaskType(declaringCompilation) &&
!returnType.IsGenericTaskType(declaringCompilation) &&
- !returnType.IsIAsyncEnumerableType(declaringCompilation);
+ !returnType.IsIAsyncEnumerableType(declaringCompilation) &&
+ !returnType.IsIAsyncEnumeratorType(declaringCompilation);
}
}
}
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 50b905a808ba7..d04b5c5127770 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -6419,7 +6419,7 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např
-
+
Návratový typ asynchronní metody musí být void, Task nebo Task<T>.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 20591719507d5..e0ab6798ef4e2 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -6419,7 +6419,7 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz
-
+
Der Rückgabetyp einer Async-Methode muss "void", "Task" oder "Task<T>" sein
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index a23c2fcd9d482..78765d70cef30 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -6419,7 +6419,7 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue
-
+
El tipo de valor devuelto de un método asincrónico debe ser void, Task o Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index 23861e4fd7810..2ec3556c8c656 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -6419,7 +6419,7 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve
-
+
Le type de retour d'une méthode async doit être void, Task ou Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 8848fc114f0d4..1684911d88272 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -6419,7 +6419,7 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn
-
+
Il tipo restituito di un metodo asincrono deve essere void, Task o Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index 68b5e9f55f87f..c5fb4fd5d9887 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -6419,7 +6419,7 @@ C# では out と ref を区別しますが、CLR では同じと認識します
-
+
非同期メソッドの戻り値の型は、void、Task、または Task<T> であることが必要です
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index 5d216143297fb..132164cf5d46e 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -6419,7 +6419,7 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간
-
+
비동기 메서드의 반환 형식은 void, Task 또는 Task<T>여야 합니다.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index 009233f4572f7..07e60d8e867f1 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -6419,7 +6419,7 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada
-
+
Zwracany typ metody asynchronicznej musi mieć wartość „void”, „Task” lub „Task<T>”.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index a123e2c4389c8..510ec43043681 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -6419,7 +6419,7 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc
-
+
O tipo de retorno de um método assíncrono deve ser void, Task ou Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index a8aaa8e0f0a87..8d6b23691aece 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi
-
+
Возвращаемым типом асинхронного метода должен быть void, Task или Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index f8cc406b21e99..71cdfa69594cc 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -6419,7 +6419,7 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl
-
+
Zaman uyumsuz bir yöntemin dönüş türü void, Task ve Task<T> olmalıdır>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 50196a365e89e..0f200082e6370 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi
-
+
异步方法的返回类型必须为 void、Task 或 Task<T>
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 27bfbbc758277..9c4342c924718 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi
-
+
非同步方法的傳回類型必須為 void、Task 或 Task<T>
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
index 69c0a8d88c03d..9a06fc1620134 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
@@ -1,15 +1,16 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
+using static Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.Instruction;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
- using static Instruction;
internal enum Instruction
{
Write,
@@ -22,35 +23,49 @@ internal enum Instruction
[CompilerTrait(CompilerFeature.AsyncStreams)]
public class CodeGenAsyncIteratorTests : EmitMetadataTestBase
{
- private void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected)
- {
- var lib = CreateCompilationWithAsyncIterator("");
- var lib_ref = lib.EmitToImageReference();
-
- string source = @"
+ private const string _enumerable = @"
using System.Threading.Tasks;
class C
{
async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
}
";
+ private const string _enumerator = @"
+using System.Threading.Tasks;
+class C
+{
+ async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; }
+}
+";
+ private static void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected)
+ {
+ foreach (var source in new[] { _enumerable, _enumerator })
+ {
+ VerifyMissingMember(source, member, expected);
+ }
+ }
+
+ private static void VerifyMissingMember(string source, WellKnownMember member, params DiagnosticDescription[] expected)
+ {
+ var lib = CreateCompilationWithTasksExtensions(AsyncStreamsTypes);
+ var lib_ref = lib.EmitToImageReference();
var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref });
comp.MakeMemberMissing(member);
comp.VerifyEmitDiagnostics(expected);
}
- private void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected)
+ private static void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected)
{
- var lib = CreateCompilationWithAsyncIterator("");
- var lib_ref = lib.EmitToImageReference();
+ foreach (var source in new[] { _enumerable, _enumerator })
+ {
+ VerifyMissingType(source, type, expected);
+ }
+ }
- string source = @"
-using System.Threading.Tasks;
-class C
-{
- async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
-}
-";
+ private static void VerifyMissingType(string source, WellKnownType type, params DiagnosticDescription[] expected)
+ {
+ var lib = CreateCompilationWithTasksExtensions(AsyncStreamsTypes);
+ var lib_ref = lib.EmitToImageReference();
var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref });
comp.MakeTypeMissing(type);
comp.VerifyEmitDiagnostics(expected);
@@ -320,9 +335,6 @@ static async System.Collections.Generic.IAsyncEnumerable M()
// (4,45): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?)
// static async System.Collections.Generic.IAsyncEnumerable M()
Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(4, 45),
- // (4,67): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
- // static async System.Collections.Generic.IAsyncEnumerable M()
- Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 67),
// (4,67): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater.
// static async System.Collections.Generic.IAsyncEnumerable M()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 67),
@@ -389,49 +401,73 @@ static async System.Collections.Generic.IAsyncEnumerable M()
Assert.True(m.IsIterator);
}
- [ConditionalFact(typeof(WindowsDesktopOnly))]
- public void AttributesSynthesized()
+ [Fact]
+ public void ReturnAfterAwait()
{
string source = @"
-public class C
+class C
{
- public static async System.Collections.Generic.IAsyncEnumerable M()
+ async System.Collections.Generic.IAsyncEnumerable M()
{
await System.Threading.Tasks.Task.CompletedTask;
- yield return 4;
+ return null;
}
}";
var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugDll);
- comp.VerifyDiagnostics();
- CompileAndVerify(comp, symbolValidator: module =>
- {
- var method = module.GlobalNamespace.GetMember("C.M");
- AssertEx.SetEqual(new[] { "AsyncStateMachineAttribute", "IteratorStateMachineAttribute" },
- GetAttributeNames(method.GetAttributes()));
- });
+ comp.VerifyDiagnostics(
+ // (7,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
+ // return null;
+ Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(7, 9)
+ );
}
[Fact]
- public void ReturnIAsyncEnumerator()
+ public void AwaitAfterReturn()
{
string source = @"
-public class C
+class C
{
- public static async System.Collections.Generic.IAsyncEnumerator M()
+ async System.Collections.Generic.IAsyncEnumerable M()
{
+ return null;
await System.Threading.Tasks.Task.CompletedTask;
- yield return 4;
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
- // (4,74): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
- // public static async System.Collections.Generic.IAsyncEnumerator M()
- Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 74),
- // (4,74): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerator' is not an iterator interface type
- // public static async System.Collections.Generic.IAsyncEnumerator M()
- Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerator").WithLocation(4, 74)
+ // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
+ // return null;
+ Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9),
+ // (7,9): warning CS0162: Unreachable code detected
+ // await System.Threading.Tasks.Task.CompletedTask;
+ Diagnostic(ErrorCode.WRN_UnreachableCode, "await").WithLocation(7, 9)
);
+
+ var m = comp.GlobalNamespace.GetMember("C.M");
+ Assert.True(m.IsAsync);
+ Assert.False(m.IsIterator);
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ public void AttributesSynthesized()
+ {
+ string source = @"
+public class C
+{
+ public static async System.Collections.Generic.IAsyncEnumerable M()
+ {
+ await System.Threading.Tasks.Task.CompletedTask;
+ yield return 4;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugDll);
+ comp.VerifyDiagnostics();
+ CompileAndVerify(comp, symbolValidator: module =>
+ {
+ var method = module.GlobalNamespace.GetMember("C.M");
+ AssertEx.SetEqual(new[] { "AsyncStateMachineAttribute", "IteratorStateMachineAttribute" },
+ GetAttributeNames(method.GetAttributes()));
+ });
}
[Fact]
@@ -601,19 +637,46 @@ public void MissingTypeAndMembers_IAsyncStateMachine()
[Fact]
public void MissingTypeAndMembers_IAsyncEnumerable()
{
- VerifyMissingMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator,
+ VerifyMissingMember(_enumerable, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator,
// (5,64): error CS0656: Missing compiler required member 'System.Collections.Generic.IAsyncEnumerable`1.GetAsyncEnumerator'
// async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerable`1", "GetAsyncEnumerator").WithLocation(5, 64)
);
- VerifyMissingType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T,
- // (5,60): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
- // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
- Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(5, 60),
- // (5,60): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerable' is not an iterator interface type
+ VerifyMissingMember(_enumerator, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator);
+
+ // Since MakeTypeMissing doesn't fully simulate a type being absent (it only makes it disappear from GetWellKnownType), we specially verify missing IAsyncEnumerable since it appears in source
+ var comp1 = CreateCompilation(_enumerable);
+ comp1.VerifyDiagnostics(
+ // (5,38): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?)
// async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
- Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerable").WithLocation(5, 60)
+ Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(5, 38)
+ );
+
+ // Also verify on local functions
+ var comp2 = CreateCompilation(@"
+using System.Threading.Tasks;
+class C
+{
+ void M()
+ {
+ _ = local();
+ async System.Collections.Generic.IAsyncEnumerable local() { await Task.CompletedTask; yield return 3; }
+ }
+}
+");
+ comp2.VerifyDiagnostics(
+ // (8,42): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?)
+ // async System.Collections.Generic.IAsyncEnumerable local() { await Task.CompletedTask; yield return 3; }
+ Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42)
+ );
+
+ // And missing IAsyncEnumerator
+ var comp3 = CreateCompilation(_enumerator);
+ comp3.VerifyDiagnostics(
+ // (5,38): error CS0234: The type or namespace name 'IAsyncEnumerator<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?)
+ // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; }
+ Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerator").WithArguments("IAsyncEnumerator<>", "System.Collections.Generic").WithLocation(5, 38)
);
}
@@ -680,10 +743,7 @@ public interface IAsyncDisposable
comp.VerifyDiagnostics(
// (8,42): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?)
// async System.Collections.Generic.IAsyncEnumerable local()
- Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42),
- // (8,64): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
- // async System.Collections.Generic.IAsyncEnumerable local()
- Diagnostic(ErrorCode.ERR_BadAsyncReturn, "local").WithLocation(8, 64)
+ Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42)
);
}
@@ -702,7 +762,16 @@ public void MissingTypeAndMembers_IAsyncEnumerator()
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerator`1", "get_Current").WithLocation(5, 64)
);
- VerifyMissingType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T,
+ VerifyMissingType(_enumerator, WellKnownType.System_Collections_Generic_IAsyncEnumerator_T,
+ // (5,60): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
+ // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; }
+ Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(5, 60),
+ // (5,60): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerator' is not an iterator interface type
+ // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; }
+ Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerator").WithLocation(5, 60)
+ );
+
+ VerifyMissingType(_enumerable, WellKnownType.System_Collections_Generic_IAsyncEnumerator_T,
// (5,64): error CS0656: Missing compiler required member 'System.Collections.Generic.IAsyncEnumerable`1.GetAsyncEnumerator'
// async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerable`1", "GetAsyncEnumerator").WithLocation(5, 64),
@@ -932,6 +1001,250 @@ async System.Collections.Generic.IAsyncEnumerable M2()
);
}
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator()
+ {
+ string source = @"
+using static System.Console;
+class C
+{
+ static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ yield return value;
+ value = 5;
+ Write(""1 "");
+ await System.Threading.Tasks.Task.CompletedTask;
+ Write(""2 "");
+ yield return 3;
+ Write(""4 "");
+ yield return value;
+ }
+ static async System.Threading.Tasks.Task Main()
+ {
+ await using (var enumerator = M(0))
+ {
+ if (!await enumerator.MoveNextAsync()) throw null;
+ Write($""Value:{enumerator.Current} "");
+ if (!await enumerator.MoveNextAsync()) throw null;
+ Write($""Value:{enumerator.Current} "");
+ if (!await enumerator.MoveNextAsync()) throw null;
+ Write($""Value:{enumerator.Current} "");
+ if (await enumerator.MoveNextAsync()) throw null;
+ if (await enumerator.MoveNextAsync()) throw null;
+
+ Write(""Done"");
+ }
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe);
+ comp.VerifyDiagnostics();
+ CompileAndVerify(comp, expectedOutput: "Value:0 1 2 Value:3 4 Value:5 Done", symbolValidator: verifyMembersAndInterfaces);
+
+ void verifyMembersAndInterfaces(ModuleSymbol module)
+ {
+ var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember("C.d__0");
+ AssertEx.SetEqual(new[] {
+ "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.Value { get; }",
+ "System.Runtime.CompilerServices.AsyncVoidMethodBuilder C.d__0.<>t__builder",
+ "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.<>v__promiseOfValueOrEnd",
+ "System.Int32 C.d__0.value",
+ "C.d__0..ctor(System.Int32 <>1__state)",
+ "void C.d__0.MoveNext()",
+ "void C.d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine)",
+ "System.Threading.Tasks.ValueTask C.d__0.System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()",
+ "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current.get",
+ "System.Boolean C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(System.Int16 token)",
+ "System.Threading.Tasks.Sources.ValueTaskSourceStatus C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(System.Int16 token)",
+ "void C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(System.Action continuation, System.Object state, System.Int16 token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)",
+ "ref System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.get_Value()",
+ "System.Threading.Tasks.ValueTask C.d__0.System.IAsyncDisposable.DisposeAsync()",
+ "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current { get; }",
+ "System.Int32 C.d__0.<>1__state" },
+ type.GetMembersUnordered().Select(m => m.ToTestDisplayString()));
+
+ AssertEx.SetEqual(new[] {
+ "System.Runtime.CompilerServices.IStrongBox>",
+ "System.Runtime.CompilerServices.IAsyncStateMachine",
+ "System.IAsyncDisposable",
+ "System.Threading.Tasks.Sources.IValueTaskSource",
+ "System.Collections.Generic.IAsyncEnumerator" },
+ type.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Select(m => m.ToTestDisplayString()));
+ }
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_CSharp73()
+ {
+ string source = @"
+class C
+{
+ async System.Collections.Generic.IAsyncEnumerator M()
+ {
+ yield return 1;
+ await System.Threading.Tasks.Task.CompletedTask;
+ }
+}";
+ var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, parseOptions: TestOptions.Regular7_3);
+ comp.VerifyDiagnostics(
+ // (4,60): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater.
+ // async System.Collections.Generic.IAsyncEnumerator M()
+ Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 60),
+ // (23,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater.
+ // #nullable disable
+ Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(23, 2)
+ );
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_WithReturnOnly()
+ {
+ string source = @"
+class C
+{
+ async System.Collections.Generic.IAsyncEnumerator M()
+ {
+ return null;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (4,60): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
+ // async System.Collections.Generic.IAsyncEnumerator M()
+ Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60),
+ // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
+ // return null;
+ Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9)
+ );
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void ReturningIAsyncEnumerator_WithReturn()
+ {
+ string source = @"
+class C
+{
+ System.Collections.Generic.IAsyncEnumerator M()
+ {
+ return null;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics();
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_WithReturnAndAwait()
+ {
+ string source = @"
+class C
+{
+ static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ return value;
+ await System.Threading.Tasks.Task.CompletedTask;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
+ // return value;
+ Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9),
+ // (7,9): warning CS0162: Unreachable code detected
+ // await System.Threading.Tasks.Task.CompletedTask;
+ Diagnostic(ErrorCode.WRN_UnreachableCode, "await").WithLocation(7, 9)
+ );
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ [WorkItem(31113, "https://github.com/dotnet/roslyn/issues/31113")]
+ public void AsyncIteratorReturningEnumerator_WithoutAsync()
+ {
+ string source = @"
+class C
+{
+ static System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ yield return value;
+ await System.Threading.Tasks.Task.CompletedTask;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (7,9): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task>'.
+ // await System.Threading.Tasks.Task.CompletedTask;
+ Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await System.Threading.Tasks.Task.CompletedTask").WithArguments("System.Collections.Generic.IAsyncEnumerator").WithLocation(7, 9)
+ );
+
+ // This error message is rather poor. Tracked by https://github.com/dotnet/roslyn/issues/31113
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_WithReturnAfterAwait()
+ {
+ string source = @"
+class C
+{
+ static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ await System.Threading.Tasks.Task.CompletedTask;
+ return value;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (7,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
+ // return value;
+ Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(7, 9)
+ );
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_WithoutAwait()
+ {
+ string source = @"
+class C
+{
+ static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ yield return value;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (4,67): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
+ // static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 67)
+ );
+ }
+
+ [ConditionalFact(typeof(WindowsDesktopOnly))]
+ [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
+ public void AsyncIteratorReturningEnumerator_WithoutYield()
+ {
+ string source = @"
+class C
+{
+ static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ {
+ await System.Threading.Tasks.Task.CompletedTask;
+ }
+}";
+ var comp = CreateCompilationWithAsyncIterator(source);
+ comp.VerifyDiagnostics(
+ // (4,67): error CS0161: 'C.M(int)': not all code paths return a value
+ // static async System.Collections.Generic.IAsyncEnumerator M(int value)
+ Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M(int)").WithLocation(4, 67)
+ );
+ }
+
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void CallingMoveNextAsyncTwice()
{
@@ -1007,7 +1320,40 @@ static async System.Threading.Tasks.Task Main()
}";
var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
- CompileAndVerify(comp, expectedOutput: "1 2 Stream1:3 1 2 Stream2:3 4 2 4 2 Done");
+ CompileAndVerify(comp, expectedOutput: "1 2 Stream1:3 1 2 Stream2:3 4 2 4 2 Done", symbolValidator: verifyMembersAndInterfaces);
+
+ void verifyMembersAndInterfaces(ModuleSymbol module)
+ {
+ var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember("C.d__0");
+ AssertEx.SetEqual(new[] {
+ "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.Value { get; }",
+ "System.Runtime.CompilerServices.AsyncVoidMethodBuilder C.d__0.<>t__builder",
+ "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.<>v__promiseOfValueOrEnd",
+ "System.Int32 C.d__0.<>3__value",
+ "C.d__0..ctor(System.Int32 <>1__state)",
+ "void C.d__0.MoveNext()",
+ "void C.d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine)",
+ "System.Collections.Generic.IAsyncEnumerator C.d__0.System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator()",
+ "System.Threading.Tasks.ValueTask C.d__0.System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()",
+ "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current.get",
+ "System.Boolean C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(System.Int16 token)",
+ "System.Threading.Tasks.Sources.ValueTaskSourceStatus C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(System.Int16 token)",
+ "void C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(System.Action continuation, System.Object state, System.Int16 token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)",
+ "ref System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.get_Value()",
+ "System.Threading.Tasks.ValueTask C.d__0.System.IAsyncDisposable.DisposeAsync()",
+ "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current { get; }",
+ "System.Int32 C.d__0.<>1__state" },
+ type.GetMembersUnordered().Select(m => m.ToTestDisplayString()));
+
+ AssertEx.SetEqual(new[] {
+ "System.Runtime.CompilerServices.IStrongBox>",
+ "System.Runtime.CompilerServices.IAsyncStateMachine",
+ "System.IAsyncDisposable",
+ "System.Threading.Tasks.Sources.IValueTaskSource",
+ "System.Collections.Generic.IAsyncEnumerable",
+ "System.Collections.Generic.IAsyncEnumerator" },
+ type.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Select(m => m.ToTestDisplayString()));
+ }
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
@@ -2271,10 +2617,7 @@ async Unknown local()
comp.VerifyDiagnostics(
// (8,15): error CS0246: The type or namespace name 'Unknown' could not be found (are you missing a using directive or an assembly reference?)
// async Unknown local()
- Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown").WithLocation(8, 15),
- // (8,23): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable
- // async Unknown local()
- Diagnostic(ErrorCode.ERR_BadAsyncReturn, "local").WithLocation(8, 23)
+ Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown").WithLocation(8, 15)
);
}
}
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
index d7bb7251df5d4..3371b5639e361 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
@@ -28086,7 +28086,7 @@ async IAsyncEnumerable local()
);
}
- [Fact(Skip = "https://github.com/dotnet/roslyn/issues/31057"), CompilerTrait(CompilerFeature.AsyncStreams)]
+ [Fact, CompilerTrait(CompilerFeature.AsyncStreams)]
public void Yield_IAsyncEnumerator()
{
var source = @"
@@ -28125,7 +28125,8 @@ async IAsyncEnumerator local()
}
}
}";
- CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics(
+ var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue());
+ comp.VerifyDiagnostics(
// (8,22): warning CS8603: Possible null reference return.
// yield return null; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22),