From ed7f54ee018850cb7686265180a3d89cce870001 Mon Sep 17 00:00:00 2001 From: WeiZheng <13881045+wzchua@users.noreply.github.com> Date: Sun, 1 Sep 2024 10:09:18 +0800 Subject: [PATCH] Fix CA1850 Fixer not accounting for toplevel syntax nodes --- ...harpPreferHashDataOverComputeHash.Fixer.cs | 25 ++++- .../PreferHashDataOverComputeHashTests.cs | 104 ++++++++++++++++++ 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpPreferHashDataOverComputeHash.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpPreferHashDataOverComputeHash.Fixer.cs index 6f825d4562..278e8c420d 100644 --- a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpPreferHashDataOverComputeHash.Fixer.cs +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpPreferHashDataOverComputeHash.Fixer.cs @@ -174,12 +174,25 @@ private SyntaxNode MoveStatementsOutOfUsingStatementWithFormatting(SyntaxNode ro }); var parent = usingStatement.Parent!; - root = root.TrackNodes(parent); - var newParent = parent.TrackNodes(usingStatement); - newParent = newParent.InsertNodesBefore(newParent.GetCurrentNode(usingStatement)!, statements); - newParent = newParent.RemoveNode(newParent.GetCurrentNode(usingStatement)!, SyntaxRemoveOptions.KeepNoTrivia)! - .WithAdditionalAnnotations(Formatter.Annotation); - root = root.ReplaceNode(root.GetCurrentNode(parent)!, newParent); + if (parent is GlobalStatementSyntax target) + { + parent = parent.Parent!; + parent = parent.TrackNodes(target); + parent = parent.InsertNodesBefore(parent.GetCurrentNode(target)!, statements.Select(SyntaxFactory.GlobalStatement)); + parent = parent.RemoveNode(parent.GetCurrentNode(target)!, SyntaxRemoveOptions.KeepNoTrivia)! + .WithAdditionalAnnotations(Formatter.Annotation); + root = parent; + } + else + { + root = root.TrackNodes(parent); + var newParent = parent.TrackNodes(usingStatement); + newParent = newParent.InsertNodesBefore(newParent.GetCurrentNode(usingStatement)!, statements); + newParent = newParent.RemoveNode(newParent.GetCurrentNode(usingStatement)!, SyntaxRemoveOptions.KeepNoTrivia)! + .WithAdditionalAnnotations(Formatter.Annotation); + root = root.ReplaceNode(root.GetCurrentNode(parent)!, newParent); + } + return root; } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/PreferHashDataOverComputeHashTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/PreferHashDataOverComputeHashTests.cs index d534e02abf..ef0dcc72f8 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/PreferHashDataOverComputeHashTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/PreferHashDataOverComputeHashTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< @@ -2755,6 +2756,80 @@ await TestCSAsync( } } + [Fact] + public async Task CSharpObjectCreationUsingStatementCaseTopLevel() + { + await TestWithType(HashTypeSHA1); + await TestWithType(HashTypeSHA256); + await TestWithType(HashTypeSHA384); + await TestWithType(HashTypeSHA512); + + static async Task TestWithType(string hashType) + { + string csInput = $@" +using System; +using System.Security.Cryptography; + +var buffer = new byte[1024]; +using (var {{|#1:hasher = new {hashType}Managed()|}}) +{{ + int line1 = 20; + byte[] digest = {{|#0:hasher.ComputeHash(buffer)|}}; + int line2 = 10; +}} + +var buffer2 = new byte[1024]; +using (var {{|#3:hasher2 = new {hashType}Managed()|}}) +{{ + int line12 = 20; + byte[] digest2 = {{|#2:hasher2.ComputeHash(buffer2, 0, 10)|}}; + int line22 = 10; +}} + +var buffer3 = new byte[1024]; +using (var {{|#5:hasher3 = new {hashType}Managed()|}}) +{{ + int line13 = 20; + byte[] digest3 = new byte[1024]; + int line23 = 10; + if ({{|#4:hasher3.TryComputeHash(buffer3, digest3, out var i)|}}) + {{ + int line33 = 10; + }} + int line43 = 10; +}} +"; + string csFix = $@" +using System; +using System.Security.Cryptography; + +var buffer = new byte[1024]; +int line1 = 20; +byte[] digest = {hashType}.HashData(buffer); +int line2 = 10; + +var buffer2 = new byte[1024]; +int line12 = 20; +byte[] digest2 = {hashType}.HashData(buffer2.AsSpan(0, 10)); +int line22 = 10; + +var buffer3 = new byte[1024]; +int line13 = 20; +byte[] digest3 = new byte[1024]; +int line23 = 10; +if ({hashType}.TryHashData(buffer3, digest3, out var i)) +{{ + int line33 = 10; +}} +int line43 = 10; +"; + await TestCSTopLevelAsync( + csInput, + csFix, + GetCreationSingleInvokeCSDiagnostics($"System.Security.Cryptography.{hashType}")); + } + } + [Fact] public async Task BasicObjectCreationUsingBlockCase() { @@ -3178,6 +3253,22 @@ private static VerifyCS.Test GetTestCS(string source, string corrected, Referenc return test; } + private static VerifyCS.Test GetTestTopLevelCS(string source, string corrected, ReferenceAssemblies referenceAssemblies) + { + var test = new VerifyCS.Test + { + TestCode = source, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + }, + ReferenceAssemblies = referenceAssemblies, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.Preview, + FixedCode = corrected, + }; + return test; + } + private static async Task TestCSAsync(string source) { await GetTestCS(source, source, ReferenceAssemblies.Net.Net50).RunAsync(); @@ -3198,6 +3289,19 @@ private static async Task TestCSAsync(string source, string corrected, params Di await GetTestCS(source, source, ReferenceAssemblies.NetCore.NetCoreApp31).RunAsync(); } + private static async Task TestCSTopLevelAsync(string source, string corrected, params DiagnosticResult[] diagnosticResults) + { + var test = GetTestTopLevelCS(source, corrected, ReferenceAssemblies.Net.Net80); + + for (int i = 0; i < diagnosticResults.Length; i++) + { + var expected = diagnosticResults[i]; + test.ExpectedDiagnostics.Add(expected); + } + + await test.RunAsync(); + } + private static VerifyVB.Test GetTestVB(string source, string corrected, ReferenceAssemblies referenceAssemblies) { var test = new VerifyVB.Test