From 8fa0df6b3eb499891700787eca8cf086d89b4603 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 10 Jul 2026 08:54:26 -0600 Subject: [PATCH] Restore VSTHRD200 API compatibility IAsyncEnumerator-returning APIs include shipped members whose names cannot be changed without a breaking change. Exclude them from VSTHRD200 while retaining coverage for the exemption. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Utils.cs | 6 ++--- ...00UseAsyncNamingConventionAnalyzerTests.cs | 27 ++----------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs index 9a2c376f4..047c50c0a 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)) - || IsIAsyncEnumerableOrEnumerator(typeSymbol) || typeSymbol.AllInterfaces.Any(IsIAsyncEnumerableOrEnumerator) + || IsIAsyncEnumerable(typeSymbol) || typeSymbol.AllInterfaces.Any(IsIAsyncEnumerable) || typeSymbol.GetAttributes().Any(ad => ad.AttributeClass?.Name == Types.AsyncMethodBuilderAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.AsyncMethodBuilderAttribute.Namespace)); - static bool IsIAsyncEnumerableOrEnumerator(ITypeSymbol symbol) - => (symbol.Name == "IAsyncEnumerable" || symbol.Name == "IAsyncEnumerator") // TODO: Use nameof after upgrade to netstandard2.1 + static bool IsIAsyncEnumerable(ITypeSymbol symbol) + => symbol.Name == "IAsyncEnumerable" // TODO: Use nameof(IAsyncEnumerable) 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 2e852c49c..9b343345f 100644 --- a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs @@ -190,29 +190,6 @@ 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() { @@ -416,13 +393,13 @@ class Test { } [Fact] - public async Task IAsyncEnumeratorOfTReturningMethodWithSuffix_GeneratesNoWarning() + public async Task IAsyncEnumeratorOfTReturningMethodWithoutSuffix_GeneratesNoWarning() { var test = @" using System.Collections.Generic; class Test { - IAsyncEnumerator FooAsync() => null; + IAsyncEnumerator GetEnumerator() => null; } "; await CSVerify.VerifyAnalyzerAsync(test);