未验证 提交 bcb422be 编写于 作者: J Julien Couvreur 提交者: GitHub

Async-streams: Fix crash with async-iterator method with only throw (#31580)

上级 436671f5
......@@ -8178,6 +8178,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to The body of an async-iterator method must contain a &apos;yield&apos; statement..
/// </summary>
internal static string ERR_PossibleAsyncIteratorWithoutYield {
get {
return ResourceManager.GetString("ERR_PossibleAsyncIteratorWithoutYield", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The body of an async-iterator method must contain a &apos;yield&apos; statement. Consider removing &apos;async&apos; from the method declaration or adding a &apos;yield&apos; statement..
/// </summary>
internal static string ERR_PossibleAsyncIteratorWithoutYieldOrAwait {
get {
return ResourceManager.GetString("ERR_PossibleAsyncIteratorWithoutYieldOrAwait", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to To cast a negative value, you must enclose the value in parentheses..
/// </summary>
......
......@@ -2641,6 +2641,12 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
<data name="ERR_AwaitForEachMissingMemberWrongAsync" xml:space="preserve">
<value>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'?</value>
</data>
<data name="ERR_PossibleAsyncIteratorWithoutYield" xml:space="preserve">
<value>The body of an async-iterator method must contain a 'yield' statement.</value>
</data>
<data name="ERR_PossibleAsyncIteratorWithoutYieldOrAwait" xml:space="preserve">
<value>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</value>
</data>
<data name="WRN_BadXMLRefParamType" xml:space="preserve">
<value>Invalid type for parameter {0} in XML comment cref attribute: '{1}'</value>
</data>
......
......@@ -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,
......
......@@ -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)
......
......@@ -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);
}
/// <summary>
/// Note: do not use a static/singleton instance of this type, as it holds state.
/// </summary>
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;
}
}
}
}
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
......@@ -152,6 +152,16 @@
<target state="new">An out variable cannot be declared as a ref local</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYield">
<source>The body of an async-iterator method must contain a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_PossibleAsyncIteratorWithoutYieldOrAwait">
<source>The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</source>
<target state="new">The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.</target>
<note />
</trans-unit>
<trans-unit id="ERR_RefAssignNarrower">
<source>Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</source>
<target state="new">Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'.</target>
......
// 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<int> 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<int> 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<int> 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<int> 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<int> M()
Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerable<int>").WithLocation(4, 74)
);
var m = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("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<int> 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<int> 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<int> 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<int> M()
Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator<int>").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<int> 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<int> M()
Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYield, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator<int>").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<int> M()
{
System.Func<System.Threading.Tasks.Task> 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<int> 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<int> 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<int> M()
Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerator<int>").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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册