From bcb422be5df09ea3645f66e1e497073ccd8c6056 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 13 Dec 2018 19:04:01 -0800 Subject: [PATCH] Async-streams: Fix crash with async-iterator method with only throw (#31580) --- .../Portable/CSharpResources.Designer.cs | 18 ++ .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../AsyncRewriter.AsyncIteratorRewriter.cs | 1 + .../Lowering/AsyncRewriter/AsyncRewriter.cs | 36 +++- .../Portable/xlf/CSharpResources.cs.xlf | 10 ++ .../Portable/xlf/CSharpResources.de.xlf | 10 ++ .../Portable/xlf/CSharpResources.es.xlf | 10 ++ .../Portable/xlf/CSharpResources.fr.xlf | 10 ++ .../Portable/xlf/CSharpResources.it.xlf | 10 ++ .../Portable/xlf/CSharpResources.ja.xlf | 10 ++ .../Portable/xlf/CSharpResources.ko.xlf | 10 ++ .../Portable/xlf/CSharpResources.pl.xlf | 10 ++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 ++ .../Portable/xlf/CSharpResources.ru.xlf | 10 ++ .../Portable/xlf/CSharpResources.tr.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 ++ .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 156 ++++++++++++++++++ 19 files changed, 348 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index e05b0f2df6c..d7a7a3559dc 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -8178,6 +8178,24 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to The body of an async-iterator method must contain a 'yield' statement.. + /// + internal static string ERR_PossibleAsyncIteratorWithoutYield { + get { + return ResourceManager.GetString("ERR_PossibleAsyncIteratorWithoutYield", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.. + /// + internal static string ERR_PossibleAsyncIteratorWithoutYieldOrAwait { + get { + return ResourceManager.GetString("ERR_PossibleAsyncIteratorWithoutYieldOrAwait", resourceCulture); + } + } + /// /// Looks up a localized string similar to To cast a negative value, you must enclose the value in parentheses.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 81276ca9c0b..fe023b115e9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2641,6 +2641,12 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Asynchronous foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'await foreach'? + + The body of an async-iterator method must contain a 'yield' statement. + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + Invalid type for parameter {0} in XML comment cref attribute: '{1}' diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 816e871e2f9..0573b61a5ea 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1594,6 +1594,8 @@ internal enum ErrorCode ERR_BadDynamicAwaitForEach = 8416, ERR_NoConvToIAsyncDispWrongAsync = 8417, ERR_NoConvToIDispWrongAsync = 8418, + ERR_PossibleAsyncIteratorWithoutYield = 8419, + ERR_PossibleAsyncIteratorWithoutYieldOrAwait = 8420, WRN_ConvertingNullableToNonNullable = 8600, WRN_NullReferenceAssignment = 8601, diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs index 7414398c061..eb9335e33dc 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs @@ -36,6 +36,7 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter Debug.Assert(method.IteratorElementType != null); _isEnumerable = method.IsIAsyncEnumerableReturningAsync(method.DeclaringCompilation); + Debug.Assert(_isEnumerable != method.IsIAsyncEnumeratorReturningAsync(method.DeclaringCompilation)); } protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag) diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs index ad064d745a5..01fad9695ec 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs @@ -47,6 +47,19 @@ internal partial class AsyncRewriter : StateMachineRewriter return body; } + CSharpCompilation compilation = method.DeclaringCompilation; + bool isAsyncEnumerableOrEnumerator = method.IsIAsyncEnumerableReturningAsync(compilation) || + method.IsIAsyncEnumeratorReturningAsync(compilation); + if (isAsyncEnumerableOrEnumerator && !method.IsIterator) + { + bool containsAwait = AwaitDetector.ContainsAwait(body); + diagnostics.Add(containsAwait ? ErrorCode.ERR_PossibleAsyncIteratorWithoutYield : ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, + method.Locations[0], method.ReturnType); + + stateMachineType = null; + return body; + } + // The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class. // For async-iterators, we also need to generate a class. var typeKind = (compilationState.Compilation.Options.EnableEditAndContinue || method.IsIterator) ? TypeKind.Class : TypeKind.Struct; @@ -56,7 +69,7 @@ internal partial class AsyncRewriter : StateMachineRewriter stateMachineType = new AsyncStateMachine(slotAllocatorOpt, compilationState, method, methodOrdinal, typeKind); compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType); - AsyncRewriter rewriter = method.IsIterator + AsyncRewriter rewriter = isAsyncEnumerableOrEnumerator ? new AsyncIteratorRewriter(bodyWithAwaitLifted, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) : new AsyncRewriter(bodyWithAwaitLifted, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics); @@ -257,5 +270,26 @@ protected virtual void GenerateMoveNext(SynthesizedImplementationMethod moveNext rewriter.GenerateMoveNext(body, moveNextMethod); } + + /// + /// Note: do not use a static/singleton instance of this type, as it holds state. + /// + private class AwaitDetector : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator + { + private bool _sawAwait; + + public static bool ContainsAwait(BoundNode node) + { + var detector = new AwaitDetector(); + detector.Visit(node); + return detector._sawAwait; + } + + public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) + { + _sawAwait = true; + return null; + } + } } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9064e4ebae2..a82eb1204a2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index a12300ab374..d36deb28c78 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 7fe0d51c1e2..1e3bb981c85 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index a7474686e2d..527935f9952 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 6b6182ca66b..9f814c81b99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index f1a9c82dc03..336d53dd477 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index c3b4732aefb..d7b11e7f93c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index be44d5a8ba3..39784ef53d3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 898dd2dedc4..525e856a783 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 97aa4bf13b9..0824142a83c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 8eb98549508..14dc0ca41f0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 3bd622e1065..2dd8238b5c6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index a664d20bc26..c3f61e36875 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -152,6 +152,16 @@ An out variable cannot be declared as a ref local + + The body of an async-iterator method must contain a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. + + + + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index e5bd118d537..5b9d30e767b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -1,5 +1,6 @@ // 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; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -1200,6 +1201,161 @@ static async System.Collections.Generic.IAsyncEnumerator M(int value) ); } + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIterator_WithThrowOnly() + { + string source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M() + { + throw new System.NotImplementedException(); + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74) + ); + comp.VerifyEmitDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), + // (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerable").WithLocation(4, 74) + ); + + var m = comp.SourceModule.GlobalNamespace.GetMember("C.M"); + Assert.False(m.IsIterator); + Assert.True(m.IsAsync); + } + + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIteratorReturningEnumerator_WithThrowOnly() + { + string source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerator M() + { + throw new System.NotImplementedException(); + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74) + ); + comp.VerifyEmitDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), + // (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing `async` from the method declaration. + // public static async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator").WithLocation(4, 74) + ); + } + + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIteratorReturningEnumerator_WithAwaitAndThrow() + { + string source = @" +class C +{ + async System.Collections.Generic.IAsyncEnumerator M() + { + await System.Threading.Tasks.Task.CompletedTask; + throw new System.NotImplementedException(); + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (4,60): error CS8419: The body of an async-iterator method must contain a 'yield' statement. + // async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYield, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator").WithLocation(4, 60) + ); + } + + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIteratorReturningEnumerator_WithThrow_WithAwaitInLambda() + { + string source = @" +class C +{ + async System.Collections.Generic.IAsyncEnumerator M() + { + System.Func lambda = async () => { await System.Threading.Tasks.Task.CompletedTask; }; + throw new System.NotImplementedException(); + } +}"; + 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) + ); + comp.VerifyEmitDiagnostics( + // (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), + // (4,60): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing `async` from the method declaration. + // async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator").WithLocation(4, 60) + ); + } + + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIterator_WithEmptyBody() + { + string source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M() + { + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), + // (4,74): error CS0161: 'C.M()': not all code paths return a value + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 74) + ); + } + + [Fact] + [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")] + public void AsyncIteratorReturningEnumerator_WithoutBody() + { + string source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerator M() + { + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,74): 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. + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), + // (4,74): error CS0161: 'C.M()': not all code paths return a value + // public static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 74) + ); + } + [ConditionalFact(typeof(WindowsDesktopOnly))] [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] public void AsyncIteratorReturningEnumerator_WithoutAwait() -- GitLab