diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATask.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATask.cs index 301015428ffa..627d06e5dc5a 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATask.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATask.cs @@ -94,12 +94,13 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeAwaitForEachLoopOperation(OperationAnalysisContext context, INamedTypeSymbol iAsyncEnumerable, INamedTypeSymbol configuredAsyncEnumerable) { - if (context.Operation is IForEachLoopOperation { IsAsynchronous: true, Collection.Type: not null } forEachOperation) + if (context.Operation is IForEachLoopOperation { IsAsynchronous: true, Collection.Type: { } collectionType } forEachOperation && + !collectionType.IsRefLikeType && + !collectionType.OriginalDefinition.Equals(configuredAsyncEnumerable, SymbolEqualityComparer.Default)) { - var collectionTypeOriginalDefinition = forEachOperation.Collection.Type.OriginalDefinition; - - if (!collectionTypeOriginalDefinition.Equals(configuredAsyncEnumerable, SymbolEqualityComparer.Default) && - context.Compilation.ClassifyCommonConversion(collectionTypeOriginalDefinition, iAsyncEnumerable) is { Exists: true, IsImplicit: true }) + // Type is itself IAsyncEnumerable or implements/extends IAsyncEnumerable + if (collectionType.OriginalDefinition.Equals(iAsyncEnumerable, SymbolEqualityComparer.Default) || + collectionType.AllInterfaces.Any(i => i.OriginalDefinition.Equals(iAsyncEnumerable, SymbolEqualityComparer.Default))) { context.ReportDiagnostic(forEachOperation.Collection.CreateDiagnostic(Rule)); } diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATaskTests.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATaskTests.cs index cd0da8681a63..63e0ff15f40d 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATaskTests.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDirectlyAwaitATaskTests.cs @@ -96,86 +96,370 @@ public async Task M() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } - [TestMethod] - [WorkItem(4888, "https://github.com/dotnet/roslyn-analyzers/issues/4888")] - public async Task CSharpAsyncDisposableAsync() + [TestMethod, WorkItem(4888, "https://github.com/dotnet/roslyn-analyzers/issues/4888")] + public Task CSharpAsyncDisposable_DiagnosticAsync() { - var code = @" -using System; -using System.Threading.Tasks; + return new VerifyCS.Test + { + TestCode = """ + using System; + using System.Threading.Tasks; -public class C -{ - private static IAsyncDisposable Create() => throw null; - private static Task CreateAsync() => throw null; + public class C + { + private static IAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; - public async Task M1() - { - await using var resource = [|Create()|]; - } + public async Task M1() + { + await using var resource = [|Create()|]; + } - public async Task M2() - { - await using var resource = [|await [|CreateAsync()|]|]; - } + public async Task M2() + { + await using var resource = [|await [|CreateAsync()|]|]; + } - public async Task M3() - { - await using (var resource = [|Create()|]) + public async Task M3() + { + await using (var resource = [|Create()|]) + { + } + } + + public async Task M4() + { + await using (var resource = [|await [|CreateAsync()|]|]) + { + } + } + } + """, + FixedCode = """ + using System; + using System.Threading.Tasks; + + public class C + { + private static IAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; + + public async Task M1() + { + await using var resource = Create().ConfigureAwait(false); + } + + public async Task M2() + { + await using var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false); + } + + public async Task M3() + { + await using (var resource = Create().ConfigureAwait(false)) + { + } + } + + public async Task M4() + { + await using (var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp8, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(CancellationToken.None); + } + + [TestMethod, WorkItem(4888, "https://github.com/dotnet/roslyn-analyzers/issues/4888")] + [DataRow("class")] + [DataRow("struct")] + [DataRow("record")] + [DataRow("record class")] + [DataRow("record struct")] + public Task CSharpAsyncDisposableImplementation_DiagnosticAsync(string implKind) { + return new VerifyCS.Test + { + TestCode = $$""" + using System; + using System.Threading.Tasks; + + public {{implKind}} MyAsyncDisposable : IAsyncDisposable + { + public ValueTask DisposeAsync() => default; + } + + public class C + { + private static MyAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; + + public async Task M1() + { + await using var resource = [|Create()|]; + } + + public async Task M2() + { + await using var resource = [|await [|CreateAsync()|]|]; + } + + public async Task M3() + { + await using (var resource = [|Create()|]) + { + } + } + + public async Task M4() + { + await using (var resource = [|await [|CreateAsync()|]|]) + { + } + } + } + """, + FixedCode = $$""" + using System; + using System.Threading.Tasks; + + public {{implKind}} MyAsyncDisposable : IAsyncDisposable + { + public ValueTask DisposeAsync() => default; + } + + public class C + { + private static MyAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; + + public async Task M1() + { + await using var resource = Create().ConfigureAwait(false); + } + + public async Task M2() + { + await using var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false); + } + + public async Task M3() + { + await using (var resource = Create().ConfigureAwait(false)) + { + } + } + + public async Task M4() + { + await using (var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(CancellationToken.None); } - } - public async Task M4() - { - await using (var resource = [|await [|CreateAsync()|]|]) + [TestMethod, WorkItem(4888, "https://github.com/dotnet/roslyn-analyzers/issues/4888")] + public Task CSharpAsyncDisposableInheritingInterface_DiagnosticAsync() { + return new VerifyCS.Test + { + TestCode = """ + using System; + using System.Threading.Tasks; + + public interface IMyAsyncDisposable : IAsyncDisposable + { + } + + public class C + { + private static IMyAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; + + public async Task M1() + { + await using var resource = [|Create()|]; + } + + public async Task M2() + { + await using var resource = [|await [|CreateAsync()|]|]; + } + + public async Task M3() + { + await using (var resource = [|Create()|]) + { + } + } + + public async Task M4() + { + await using (var resource = [|await [|CreateAsync()|]|]) + { + } + } + } + """, + FixedCode = """ + using System; + using System.Threading.Tasks; + + public interface IMyAsyncDisposable : IAsyncDisposable + { + } + + public class C + { + private static IMyAsyncDisposable Create() => throw null; + private static Task CreateAsync() => throw null; + + public async Task M1() + { + await using var resource = Create().ConfigureAwait(false); + } + + public async Task M2() + { + await using var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false); + } + + public async Task M3() + { + await using (var resource = Create().ConfigureAwait(false)) + { + } + } + + public async Task M4() + { + await using (var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(CancellationToken.None); } - } -} -"; - var fixedCode = @" -using System; -using System.Threading.Tasks; -public class C -{ - private static IAsyncDisposable Create() => throw null; - private static Task CreateAsync() => throw null; + [TestMethod, WorkItem(4888, "https://github.com/dotnet/sdk/issues/4888")] + public Task CSharpAsyncDisposableImplementationRefStruct_UsingStatement_NoDiagnosticAsync() + { + return new VerifyCS.Test + { + TestCode = """ + using System; + using System.Threading.Tasks; - public async Task M1() - { - await using var resource = Create().ConfigureAwait(false); - } + public ref struct MyAsyncDisposableLikeStruct : IAsyncDisposable + { + public ValueTask DisposeAsync() => default; + } - public async Task M2() - { - await using var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false); - } + public class C + { + public async Task M() + { + await using (var resource = new MyAsyncDisposableLikeStruct()) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80 + }.RunAsync(CancellationToken.None); + } - public async Task M3() - { - await using (var resource = Create().ConfigureAwait(false)) + [TestMethod, WorkItem(4888, "https://github.com/dotnet/sdk/issues/4888")] + public Task CSharpAsyncDisposableImplementationRefStruct_UsingDeclaration_NoDiagnosticAsync() { + return new VerifyCS.Test + { + TestCode = """ + using System; + using System.Threading.Tasks; + + public ref struct MyAsyncDisposableLikeStruct : IAsyncDisposable + { + public ValueTask DisposeAsync() => default; + } + + public class C + { + public async Task M() + { + await using var resource = new MyAsyncDisposableLikeStruct(); + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80 + }.RunAsync(CancellationToken.None); } - } - public async Task M4() - { - await using (var resource = (await CreateAsync().ConfigureAwait(false)).ConfigureAwait(false)) + [TestMethod, WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] + public Task CSharpPatternBasedAwaitUsing_UsingStatement_NoDiagnosticAsync() { + return new VerifyCS.Test + { + TestCode = """ + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + + public struct CustomAsyncDisposable + { + public ConfiguredValueTaskAwaitable DisposeAsync() => default; + } + + public class C + { + public async Task M() + { + await using (var cad = new CustomAsyncDisposable()) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(CancellationToken.None); } - } -} -"; - await new VerifyCS.Test + [TestMethod, WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] + public Task CSharpPatternBasedAwaitUsing_UsingDeclaration_NoDiagnosticAsync() + { + return new VerifyCS.Test { - ReferenceAssemblies = ReferenceAssemblies.Default.AddPackages( - ImmutableArray.Create(new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "5.0.0"))), - LanguageVersion = LanguageVersion.CSharp8, - TestCode = code, - FixedCode = fixedCode, + TestCode = """ + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + + public struct CustomAsyncDisposable + { + public ConfiguredValueTaskAwaitable DisposeAsync() => default; + } + + public class C + { + public async Task M() + { + await using var cad = new CustomAsyncDisposable(); + } + } + """, + LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(CancellationToken.None); } @@ -345,9 +629,9 @@ End Class Public Class SomeAwaiter Implements INotifyCompletion Public ReadOnly Property IsCompleted() As Boolean - Get - Throw New NotImplementedException() - End Get + Get + Throw New NotImplementedException() + End Get End Property Public Sub OnCompleted(continuation As Action) Implements INotifyCompletion.OnCompleted @@ -741,117 +1025,304 @@ async Task CoreAsync() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } - [TestMethod, WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] - public Task CsharpAwaitIAsyncEnumerable_DiagnosticAsync() + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerable_DiagnosticAsync(bool genericMethod) { return new VerifyCS.Test { - TestCode = @" -using System.Collections.Generic; -using System.Threading.Tasks; + TestCode = $$""" + using System.Collections.Generic; + using System.Threading.Tasks; -public class C -{ - public async Task Test(IAsyncEnumerable enumerable) - { - await foreach(var i in [|enumerable|]) - { - } - } -}", - FixedCode = @" -using System.Collections.Generic; -using System.Threading.Tasks; + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in [|enumerable|]) + { + } + } + } + """, + FixedCode = $$""" + using System.Collections.Generic; + using System.Threading.Tasks; -public class C -{ - public async Task Test(IAsyncEnumerable enumerable) - { - await foreach(var i in enumerable.ConfigureAwait(false)) - { - } - } -}", + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in enumerable.ConfigureAwait(false)) + { + } + } + } + """, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(CancellationToken.None); } - [TestMethod, WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] - [DataRow("true")] - [DataRow("false")] - public Task CsharpAwaitIAsyncEnumerable_NoDiagnosticAsync(string continueOnCapturedContext) + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerableImplementation_Concrete_DiagnosticAsync( + [CombinatorialValues("class", "struct", "record", "record class", "record struct")] string implKind) { return new VerifyCS.Test { - TestCode = @$" -using System.Collections.Generic; -using System.Threading.Tasks; + TestCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; -public class C -{{ - public async Task Test(IAsyncEnumerable enumerable) - {{ - await foreach(var i in enumerable.ConfigureAwait({continueOnCapturedContext})) - {{ - }} - }} -}}", - LanguageVersion = LanguageVersion.CSharp8 + public {{implKind}} MyAsyncEnumerable : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; + } + + public class C + { + public async Task Test(MyAsyncEnumerable enumerable) + { + await foreach (var i in [|enumerable|]) + { + } + } + } + """, + FixedCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public {{implKind}} MyAsyncEnumerable : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; + } + + public class C + { + public async Task Test(MyAsyncEnumerable enumerable) + { + await foreach (var i in enumerable.ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10 }.RunAsync(CancellationToken.None); } [TestMethod, WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] - public Task CsharpForEachEnumerable_NoDiagnosticAsync() + public Task CSharpAwaitIAsyncEnumerableInheritingInterface_Concrete_DiagnosticAsync() { return new VerifyCS.Test { - TestCode = @" -using System.Collections.Generic; -using System.Threading.Tasks; + TestCode = """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; -public class C -{ - public void Test(IEnumerable enumerable) - { - foreach(var i in enumerable) - { - } - } -}", - LanguageVersion = LanguageVersion.CSharp8 + public interface IMyAsyncEnumerable : IAsyncEnumerable + { + } + + public class C + { + public async Task Test(IMyAsyncEnumerable enumerable) + { + await foreach (var i in [|enumerable|]) + { + } + } + } + """, + FixedCode = """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public interface IMyAsyncEnumerable : IAsyncEnumerable + { + } + + public class C + { + public async Task Test(IMyAsyncEnumerable enumerable) + { + await foreach (var i in enumerable.ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10 }.RunAsync(CancellationToken.None); } - [TestMethod, WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] - public Task CSharpNoDiagnosticForPatternBasedAwaitUsing_UsingStatement() + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerableImplementation_Generic_DiagnosticAsync( + bool genericMethod, + [CombinatorialValues("class", "struct", "record", "record class", "record struct")] string implKind) + { + return new VerifyCS.Test + { + TestCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public {{implKind}} MyAsyncEnumerable : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; + } + + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in [|enumerable|]) + { + } + } + } + """, + FixedCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public {{implKind}} MyAsyncEnumerable : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; + } + + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in enumerable.ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10 + }.RunAsync(CancellationToken.None); + } + + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerableInheritingInterface_Generic_DiagnosticAsync(bool genericMethod) + { + return new VerifyCS.Test + { + TestCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public interface IMyAsyncEnumerable : IAsyncEnumerable + { + } + + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IMyAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in [|enumerable|]) + { + } + } + } + """, + FixedCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public interface IMyAsyncEnumerable : IAsyncEnumerable + { + } + + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(IMyAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in enumerable.ConfigureAwait(false)) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp10 + }.RunAsync(CancellationToken.None); + } + + [TestMethod, WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerableImplementationRefStruct_Concrete_NoDiagnosticAsync() { return new VerifyCS.Test { TestCode = """ - using System.Runtime.CompilerServices; + using System.Collections.Generic; + using System.Threading; using System.Threading.Tasks; - public struct CustomAsyncDisposable + public ref struct MyAsyncEnumerableLikeStruct : IAsyncEnumerable { - public ConfiguredValueTaskAwaitable DisposeAsync() => default; + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; } - public static class Class + public class C { - public static async Task Test() + public async Task Test() { - await using (var cad = new CustomAsyncDisposable()) + await foreach (var i in new MyAsyncEnumerableLikeStruct()) { } } } """, - LanguageVersion = LanguageVersion.CSharp8 + LanguageVersion = LanguageVersion.CSharp13 + }.RunAsync(CancellationToken.None); + } + + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpAwaitIAsyncEnumerableImplementationRefStruct_Generic_NoDiagnosticAsync(bool genericMethod) + { + return new VerifyCS.Test + { + TestCode = $$""" + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public ref struct MyAsyncEnumerableLikeStruct : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => null; + } + + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}() + { + MyAsyncEnumerableLikeStruct<{{(genericMethod ? "T" : "int")}}> enumerable = default; + await foreach (var i in enumerable) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp13 }.RunAsync(CancellationToken.None); } [TestMethod, WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] - public Task CSharpNoDiagnosticForPatternBasedAwaitUsing_UsingDeclaration() + public Task CSharpAwaitPatternBasedAsyncEnumerable_Concrete_NoDiagnosticAsync() { return new VerifyCS.Test { @@ -859,16 +1330,18 @@ public Task CSharpNoDiagnosticForPatternBasedAwaitUsing_UsingDeclaration() using System.Runtime.CompilerServices; using System.Threading.Tasks; - public struct CustomAsyncDisposable + public struct CustomAsyncEnumerable { - public ConfiguredValueTaskAwaitable DisposeAsync() => default; + public ConfiguredCancelableAsyncEnumerable.Enumerator GetAsyncEnumerator() => default; } - public static class Class + public class C { - public static async Task Test() + public async Task Test(CustomAsyncEnumerable enumerable) { - await using var cad = new CustomAsyncDisposable(); + await foreach (var i in enumerable) + { + } } } """, @@ -876,12 +1349,13 @@ public static async Task Test() }.RunAsync(CancellationToken.None); } - [TestMethod, WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] - public Task CSharpNoDiagnosticForPatternBasedAwaitForEach() + [TestMethod, CombinatorialData] + [WorkItem(53461, "https://github.com/dotnet/sdk/issues/53461")] + public Task CSharpAwaitPatternBasedAsyncEnumerable_Generic_NoDiagnosticAsync(bool genericMethod) { return new VerifyCS.Test { - TestCode = """ + TestCode = $$""" using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -890,11 +1364,35 @@ public struct CustomAsyncEnumerable public ConfiguredCancelableAsyncEnumerable.Enumerator GetAsyncEnumerator() => default; } - public static class Class + public class C + { + public async Task Test{{(genericMethod ? "" : "")}}(CustomAsyncEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) + { + await foreach (var i in enumerable) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(CancellationToken.None); + } + + [TestMethod, CombinatorialData] + [WorkItem(6652, "https://github.com/dotnet/roslyn-analyzers/issues/6652")] + public Task CSharpForEachEnumerable_NoDiagnosticAsync(bool genericMethod) + { + return new VerifyCS.Test + { + TestCode = $$""" + using System.Collections.Generic; + using System.Threading.Tasks; + + public class C { - public static async Task Test() + public void Test{{(genericMethod ? "" : "")}}(IEnumerable<{{(genericMethod ? "T" : "int")}}> enumerable) { - await foreach (var i in new CustomAsyncEnumerable()) + foreach(var i in enumerable) { } }