From 7dd110bc10fe426ebfe773cbdb4e585c33529525 Mon Sep 17 00:00:00 2001 From: Dennis Fischer Date: Sat, 28 Nov 2015 02:27:22 +0100 Subject: [PATCH] Enable batch fixing element order --- .../Helpers/SortingHelper.cs | 51 +++++++++++ .../ElementOrderCodeFixProvider.cs | 91 ++++++++++++++----- .../StyleCop.Analyzers.CodeFixes.csproj | 1 + .../OrderingRules/SA1203UnitTests.cs | 38 +++++++- .../OrderingRules/SA1214UnitTests.cs | 27 +++++- 5 files changed, 181 insertions(+), 27 deletions(-) create mode 100644 StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/SortingHelper.cs diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/SortingHelper.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/SortingHelper.cs new file mode 100644 index 000000000..68e01b8b8 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/SortingHelper.cs @@ -0,0 +1,51 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Helpers +{ + using System; + using System.Collections.Generic; + + internal static class SortingHelper + { + /// + /// Performs a insertion sort on . + /// + /// The type of items to sort. + /// An array of items that should be sorted. + /// A that is used to determine order. + /// A that is called when the first parameter should be moved in front of the second parameter. + /// A that contains all items in order. + /// This performs a stable sort. + /// The running time is in O(n^2). + internal static LinkedList InsertionSort(T[] data, Comparison comparer, Action moveItem) + { + LinkedList result = new LinkedList(); + for (int i = 0; i < data.Length; i++) + { + var currentItem = result.First; + bool itemMoved = false; + + while (currentItem != null) + { + if (comparer(currentItem.Value, data[i]) > 0) + { + result.AddBefore(currentItem, data[i]); + moveItem(data[i], currentItem.Value); + itemMoved = true; + break; + } + + currentItem = currentItem.Next; + } + + if (!itemMoved) + { + result.AddLast(data[i]); + } + } + + return result; + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs index 69e4c98c7..f6f99c957 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs @@ -3,6 +3,7 @@ namespace StyleCop.Analyzers.OrderingRules { + using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; @@ -78,37 +79,57 @@ private static SyntaxNode UpdateSyntaxRoot(MemberDeclarationSyntax memberDeclara var parentDeclaration = memberDeclaration.Parent; var memberToMove = new MemberOrderHelper(memberDeclaration, elementOrder); - if (parentDeclaration is TypeDeclarationSyntax) + return OrderMember(memberToMove, GetMembers(parentDeclaration), elementOrder, syntaxRoot, indentationOptions); + } + + private static SyntaxList GetMembers(SyntaxNode node) + { + if (node is TypeDeclarationSyntax) { - return HandleTypeDeclaration(memberToMove, (TypeDeclarationSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationOptions); + return ((TypeDeclarationSyntax)node).Members; } - if (parentDeclaration is NamespaceDeclarationSyntax) + if (node is NamespaceDeclarationSyntax) { - return HandleNamespaceDeclaration(memberToMove, (NamespaceDeclarationSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationOptions); + return ((NamespaceDeclarationSyntax)node).Members; } - if (parentDeclaration is CompilationUnitSyntax) + if (node is CompilationUnitSyntax) { - return HandleCompilationUnitDeclaration(memberToMove, (CompilationUnitSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationOptions); + return ((CompilationUnitSyntax)node).Members; } - return syntaxRoot; + throw new ArgumentException($"{nameof(node)} does not have a member list", nameof(node)); } - private static SyntaxNode HandleTypeDeclaration(MemberOrderHelper memberOrder, TypeDeclarationSyntax typeDeclarationNode, ImmutableArray elementOrder, SyntaxNode syntaxRoot, IndentationOptions indentationOptions) + private static SyntaxNode WithMembers(SyntaxNode node, SyntaxList members) { - return OrderMember(memberOrder, typeDeclarationNode.Members, elementOrder, syntaxRoot, indentationOptions); - } + if (node is ClassDeclarationSyntax) + { + return ((ClassDeclarationSyntax)node).WithMembers(members); + } - private static SyntaxNode HandleCompilationUnitDeclaration(MemberOrderHelper memberOrder, CompilationUnitSyntax compilationUnitDeclaration, ImmutableArray elementOrder, SyntaxNode syntaxRoot, IndentationOptions indentationOptions) - { - return OrderMember(memberOrder, compilationUnitDeclaration.Members, elementOrder, syntaxRoot, indentationOptions); - } + if (node is CompilationUnitSyntax) + { + return ((CompilationUnitSyntax)node).WithMembers(members); + } - private static SyntaxNode HandleNamespaceDeclaration(MemberOrderHelper memberOrder, NamespaceDeclarationSyntax namespaceDeclaration, ImmutableArray elementOrder, SyntaxNode syntaxRoot, IndentationOptions indentationOptions) - { - return OrderMember(memberOrder, namespaceDeclaration.Members, elementOrder, syntaxRoot, indentationOptions); + if (node is InterfaceDeclarationSyntax) + { + return ((InterfaceDeclarationSyntax)node).WithMembers(members); + } + + if (node is NamespaceDeclarationSyntax) + { + return ((NamespaceDeclarationSyntax)node).WithMembers(members); + } + + if (node is StructDeclarationSyntax) + { + return ((StructDeclarationSyntax)node).WithMembers(members); + } + + throw new ArgumentException($"{nameof(node)} does not have a member list", nameof(node)); } private static SyntaxNode OrderMember(MemberOrderHelper memberOrder, SyntaxList members, ImmutableArray elementOrder, SyntaxNode syntaxRoot, IndentationOptions indentationOptions) @@ -270,7 +291,8 @@ protected override async Task FixAllInDocumentAsync(FixAllContext fi var elementOrder = settings.OrderingRules.ElementOrder; var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false); - var trackedDiagnosticMembers = new List(); + // trackedParents are the elements that have elements that are not in order + var trackedParents = new HashSet(); foreach (var diagnostic in diagnostics) { var memberDeclaration = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); @@ -279,17 +301,40 @@ protected override async Task FixAllInDocumentAsync(FixAllContext fi continue; } - trackedDiagnosticMembers.Add(memberDeclaration); + trackedParents.Add(memberDeclaration.Parent); + } + + syntaxRoot = syntaxRoot.TrackNodes(trackedParents); + + foreach (var member in trackedParents) + { + var parent = syntaxRoot.GetCurrentNode(member); + syntaxRoot = SortChildren(parent, elementOrder, syntaxRoot, indentationOptions); } - syntaxRoot = syntaxRoot.TrackNodes(trackedDiagnosticMembers); + return syntaxRoot; + } + + private static SyntaxNode SortChildren(SyntaxNode parent, ImmutableArray elementOrder, SyntaxNode syntaxRoot, IndentationOptions indentationOptions) + { + SyntaxList memberList = GetMembers(parent); - foreach (var member in trackedDiagnosticMembers) + List orderHelpers = new List(memberList.Count); + + foreach (var member in memberList) { - var memberDeclaration = syntaxRoot.GetCurrentNode(member); - syntaxRoot = UpdateSyntaxRoot(memberDeclaration, elementOrder, syntaxRoot, indentationOptions); + orderHelpers.Add(new MemberOrderHelper(member, elementOrder)); } + syntaxRoot = syntaxRoot.TrackNodes(memberList); + + var orderHelperArray = orderHelpers.ToArray(); + Action moveMember = (i, j) => + { + syntaxRoot = MoveMember(syntaxRoot, syntaxRoot.GetCurrentNode(i.Member), syntaxRoot.GetCurrentNode(j.Member), indentationOptions); + }; + SortingHelper.InsertionSort(orderHelperArray, (a, b) => b.Priority - a.Priority, moveMember); + return syntaxRoot; } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj index 11314d4a0..b92393cec 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj @@ -66,6 +66,7 @@ + diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1203UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1203UnitTests.cs index 651ba9f1e..9822399c8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1203UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1203UnitTests.cs @@ -285,6 +285,22 @@ public async Task TestCodeFixAsync() public int between; } +"; + + var batchFixedTestCode = @"public class Foo +{ + public const string Before2 = ""test""; + + public const string After2 = ""test""; + + public int between; + + private const string Before1 = ""test""; + + private const string After1 = ""test""; + + private int field1; +} "; var diagnosticResults = new[] @@ -294,7 +310,7 @@ public async Task TestCodeFixAsync() }; await this.VerifyCSharpDiagnosticAsync(testCode, diagnosticResults, CancellationToken.None).ConfigureAwait(false); await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); - await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedTestCode, batchFixedTestCode).ConfigureAwait(false); } /// @@ -336,6 +352,23 @@ public async Task TestCodeFixWithCommentsAsync() public int between; } +"; + + var batchFixedTestCode = @"public class Foo +{ + public const string Before2 = ""test""; + + public const string After2 = ""test""; + + public int between; + + private const string Before1 = ""test""; + + //Comment on this field + private const string After1 = ""test""; + + private int field1; +} "; var diagnosticResults = new[] @@ -345,7 +378,8 @@ public async Task TestCodeFixWithCommentsAsync() }; await this.VerifyCSharpDiagnosticAsync(testCode, diagnosticResults, CancellationToken.None).ConfigureAwait(false); await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); - await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(batchFixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedTestCode, batchFixedTestCode).ConfigureAwait(false); } /// diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1214UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1214UnitTests.cs index e4034c5a4..c14bbb9ec 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1214UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1214UnitTests.cs @@ -229,7 +229,7 @@ public void Ff() {} public static readonly int u = 5; - public class FooInner + public class FooInner { private int aa = 0; public static readonly int t = 2; @@ -273,8 +273,31 @@ public class FooInner public static readonly int j = 0; }"; + var batchFixTestCode = @" +public class Foo +{ + public static readonly int u = 5; + + public static readonly int j = 0; + + public static string s2 = ""qwe""; + + public string s = ""qwe""; + private static readonly int i = 0; + + public void Ff() {} + + public class FooInner + { + public static readonly int t = 2; + private static readonly int e = 1; + private static int z = 999; + private int aa = 0; + } +}"; + await this.VerifyCSharpDiagnosticAsync(fixTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); - await this.VerifyCSharpFixAsync(testCode, fixTestCode).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixTestCode, batchFixTestCode).ConfigureAwait(false); } [Fact]