From 5c5ae953ccb8421086c14d3bf35cb2baf3cdf7d0 Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Fri, 24 May 2024 17:39:54 +0200 Subject: [PATCH 1/2] Don't raise CA1849 when async version has fewer parameters --- .../Runtime/UseAsyncMethodInAsyncContext.cs | 26 +++++++++++++++++- .../UseAsyncMethodInAsyncContextTests.cs | 27 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs index a720990d51..ff161654d6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs @@ -235,7 +235,31 @@ private static ImmutableArray GetExcludedMethods(WellKnownTypePro /// private static bool HasSupersetOfParameterTypes(IMethodSymbol candidateMethod, IMethodSymbol baselineMethod) { - return candidateMethod.Parameters.All(candidateParameter => candidateParameter.HasExplicitDefaultValue || baselineMethod.Parameters.Any(baselineParameter => baselineParameter.Type?.Equals(candidateParameter.Type) ?? false)); + var baselineMethodTypeCount = GetTypeCount(baselineMethod); + var candidateMethodTypeCount = GetTypeCount(candidateMethod); + foreach (var (type, baselineCount) in baselineMethodTypeCount) + { + if(!candidateMethodTypeCount.TryGetValue(type, out var candidateCount) || baselineCount > candidateCount) + { + return false; + } + } + + return true; + + // Returns a dictionary of a count of the method's non-optional parameters by type. + // For a method with the signature: + // void M(string s, string s2, bool b, char c = 'c') + // the dictionary would contain the following key value pairs: + // - (string, 2) + // - (bool, 1) + static IDictionary GetTypeCount(IMethodSymbol methodSymbol) + { + return methodSymbol.Parameters + .Where(p => !p.HasExplicitDefaultValue) + .GroupBy(p => p.Type, SymbolEqualityComparer.Default) + .ToDictionary, ITypeSymbol, int>(g => g.Key, g => g.Count(), SymbolEqualityComparer.Default); + } } private static bool HasAsyncCompatibleReturnType(IMethodSymbol methodSymbol, ConcurrentDictionary syncBlockingTypes) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContextTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContextTests.cs index f9e050ca07..c5e386757b 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContextTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContextTests.cs @@ -1343,6 +1343,33 @@ public async Task Foo() return VerifyCS.VerifyAnalyzerAsync(code); } + [Theory] + [InlineData("string s", "")] + [InlineData("string s", "bool b")] + [InlineData("string s", "bool b = true")] + [WorkItem(7289, "https://github.com/dotnet/roslyn-analyzers/issues/7289")] + public Task WhenAsyncVersionHasFewerParameters_NoDiagnostic(string syncParameters, string asyncParameters) + { + var code = $$""" + using System.Threading.Tasks; + + class Test + { + private void Run({{syncParameters}}) { } + private Task RunAsync({{asyncParameters}}) => Task.CompletedTask; + + private async Task ReproAsync() + { + await Task.Yield(); + + Run(""); + } + } + """; + + return CreateCSTestAndRunAsync(code); + } + private static async Task CreateCSTestAndRunAsync(string testCS) { var csTestVerify = new VerifyCS.Test From ac3b9a94829f703c46ad7a3acf3207e211313ed9 Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Fri, 24 May 2024 18:27:24 +0200 Subject: [PATCH 2/2] Fix formatting --- .../Runtime/UseAsyncMethodInAsyncContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs index ff161654d6..fd6c92b201 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseAsyncMethodInAsyncContext.cs @@ -239,7 +239,7 @@ private static bool HasSupersetOfParameterTypes(IMethodSymbol candidateMethod, I var candidateMethodTypeCount = GetTypeCount(candidateMethod); foreach (var (type, baselineCount) in baselineMethodTypeCount) { - if(!candidateMethodTypeCount.TryGetValue(type, out var candidateCount) || baselineCount > candidateCount) + if (!candidateMethodTypeCount.TryGetValue(type, out var candidateCount) || baselineCount > candidateCount) { return false; }