diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs index 047c50c0a..9a2c376f4 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs @@ -293,11 +293,11 @@ public static bool IsAsyncCompatibleReturnType([NotNullWhen(true)] this ITypeSym // ValueTask and ValueTask have the AsyncMethodBuilderAttribute. return (typeSymbol.Name == nameof(Task) && typeSymbol.BelongsToNamespace(Namespaces.SystemThreadingTasks)) - || IsIAsyncEnumerable(typeSymbol) || typeSymbol.AllInterfaces.Any(IsIAsyncEnumerable) + || IsIAsyncEnumerableOrEnumerator(typeSymbol) || typeSymbol.AllInterfaces.Any(IsIAsyncEnumerableOrEnumerator) || typeSymbol.GetAttributes().Any(ad => ad.AttributeClass?.Name == Types.AsyncMethodBuilderAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.AsyncMethodBuilderAttribute.Namespace)); - static bool IsIAsyncEnumerable(ITypeSymbol symbol) - => symbol.Name == "IAsyncEnumerable" // TODO: Use nameof(IAsyncEnumerable) after upgrade to netstandard2.1 + static bool IsIAsyncEnumerableOrEnumerator(ITypeSymbol symbol) + => (symbol.Name == "IAsyncEnumerable" || symbol.Name == "IAsyncEnumerator") // TODO: Use nameof after upgrade to netstandard2.1 && symbol.BelongsToNamespace(Namespaces.SystemCollectionsGeneric); } diff --git a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs index 9e4d60ac6..2e852c49c 100644 --- a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs @@ -190,6 +190,29 @@ class Test { await CSVerify.VerifyCodeFixAsync(test, expected, withFix); } + [Fact] + public async Task IAsyncEnumeratorOfTReturningMethodWithoutSuffix_GeneratesWarning() + { + var test = @" +using System.Collections.Generic; + +class Test { + IAsyncEnumerator Foo() => default; +} +"; + + var withFix = @" +using System.Collections.Generic; + +class Test { + IAsyncEnumerator FooAsync() => default; +} +"; + + DiagnosticResult expected = CSVerify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 27, 5, 30); + await CSVerify.VerifyCodeFixAsync(test, expected, withFix); + } + [Fact] public async Task HomemadeIAsyncEnumerableOfTReturningMethodWithoutSuffix_GeneratesWarning() { @@ -392,6 +415,19 @@ class Test { await CSVerify.VerifyAnalyzerAsync(test); } + [Fact] + public async Task IAsyncEnumeratorOfTReturningMethodWithSuffix_GeneratesNoWarning() + { + var test = @" +using System.Collections.Generic; + +class Test { + IAsyncEnumerator FooAsync() => null; +} +"; + await CSVerify.VerifyAnalyzerAsync(test); + } + [Fact] public async Task HomemadeIAsyncEnumerableOfTReturningMethodWithSuffix_GeneratesNoWarning() {