Skip to content

Commit

Permalink
Sort static members first (#4)
Browse files Browse the repository at this point in the history
This change changes the sort algorithm to sort by type, accessibility,
static, and then name. Fixes #3.

#do release
  • Loading branch information
craigktreasure authored Oct 2, 2023
1 parent ad2df73 commit 3b80a7b
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ private static async Task<Document> ReorderMembersAsync(Document document, Class
{
SyntaxList<MemberDeclarationSyntax> members = classDeclaration.Members;
List<MemberDeclarationSyntax> sortedMembers = members
.OrderBy(MemberOrderAnalyzer.GetMemberCategory)
.ThenBy(MemberOrderAnalyzer.GetAccessibilityModifier)
.OrderBy(MemberOrderAnalyzer.GetMemberCategoryOrder)
.ThenBy(MemberOrderAnalyzer.GetAccessibilityModifierOrder)
.ThenBy(MemberOrderAnalyzer.GetStaticOrder)
.ThenBy(MemberOrderAnalyzer.GetMemberName)
.ToList();
TryKeepWhiteSpace(ref members, sortedMembers);
Expand Down
35 changes: 31 additions & 4 deletions src/MemberOrder/MemberOrder/MemberOrderAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ public class MemberOrderAnalyzer : DiagnosticAnalyzer
/// </summary>
/// <param name="member">The member.</param>
/// <returns><see cref="int"/>.</returns>
public static int GetAccessibilityModifier(MemberDeclarationSyntax member)
public static int GetAccessibilityModifierOrder(MemberDeclarationSyntax member)
{
if (member is null)
{
throw new ArgumentNullException(nameof(member));
}

if (member is ConstructorDeclarationSyntax && member.Modifiers.Any(SyntaxKind.StaticKeyword))
{
// Special case static constructors to come first.
return -1;
}

// public
if (member.Modifiers.Any(SyntaxKind.PublicKeyword))
{
Expand Down Expand Up @@ -96,7 +102,7 @@ public static int GetAccessibilityModifier(MemberDeclarationSyntax member)
/// </summary>
/// <param name="member">The member.</param>
/// <returns><see cref="int"/>.</returns>
public static int GetMemberCategory(MemberDeclarationSyntax member)
public static int GetMemberCategoryOrder(MemberDeclarationSyntax member)
{
if (member is null)
{
Expand Down Expand Up @@ -145,6 +151,26 @@ public static string GetMemberName(MemberDeclarationSyntax member)
};
}

/// <summary>
/// Gets the static order.
/// </summary>
/// <param name="member">The member.</param>
/// <returns><see cref="int"/>.</returns>
public static int GetStaticOrder(MemberDeclarationSyntax member)
{
if (member is null)
{
throw new ArgumentNullException(nameof(member));
}

if (member.Modifiers.Any(SyntaxKind.StaticKeyword))
{
return 0;
}

return 1;
}

/// <summary>
/// Initializes the specified context.
/// </summary>
Expand All @@ -168,8 +194,9 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
Location classLocation = classDeclaration.GetLocation();
SyntaxList<MemberDeclarationSyntax> members = classDeclaration.Members;
List<MemberDeclarationSyntax> sortedMembers = members
.OrderBy(GetMemberCategory)
.ThenBy(GetAccessibilityModifier)
.OrderBy(GetMemberCategoryOrder)
.ThenBy(GetAccessibilityModifierOrder)
.ThenBy(GetStaticOrder)
.ThenBy(GetMemberName)
.ToList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ class MyClass
await VerifyCSCodeFix.VerifyCodeFixAsync(test, expected, fixtest);
}

[TestMethod]
public async Task CodeFix_FieldsOutOfStaticOrder_Reordered()
{
string test = @"
class MyClass
{
public int myPublicFieldA;
public static int myPublicStaticFieldA;
}";

string fixtest = @"
class MyClass
{
public static int myPublicStaticFieldA;
public int myPublicFieldA;
}";

DiagnosticResult expected = VerifyCSCodeFix.Diagnostic(MemberOrderAnalyzer.DiagnosticId)
.WithLocation("", 2, 9)
.WithArguments("MyClass");
await VerifyCSCodeFix.VerifyCodeFixAsync(test, expected, fixtest);
}

[TestMethod]
public async Task CodeFix_MembersOutOfTypeOrder_Reordered()
{
Expand Down
71 changes: 71 additions & 0 deletions tests/MemberOrder/MemberOrder.Tests/MemberOrderUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,54 @@ public async Task Analyzer_Class_AllCategoriesModifiersAndNamesInOrder_NoDiagnos
public class MyClass
{
// Fields
public static int myPublicStaticFieldA;
public static int myPublicStaticFieldB;
public int myPublicFieldA;
public int myPublicFieldB;
internal static int myInternalStaticFieldA;
internal static int myInternalStaticFieldB;
internal int myInternalFieldA;
internal int myInternalFieldB;
protected internal static int myProtectedInternalStaticFieldA;
protected internal static int myProtectedInternalStaticFieldB;
protected internal int myProtectedInternalFieldA;
protected internal int myProtectedInternalFieldB;
private protected static int myPrivateProtectedStaticFieldA;
private protected static int myPrivateProtectedStaticFieldB;
private protected int myPrivateProtectedFieldA;
private protected int myPrivateProtectedFieldB;
protected static int myProtectedStaticFieldA;
protected static int myProtectedStaticFieldB;
protected int myProtectedFieldA;
protected int myProtectedFieldB;
private static int myPrivateStaticFieldA;
private static int myPrivateStaticFieldB;
private int myPrivateFieldA;
private int myPrivateFieldB;
// Properties
public static int MyPublicStaticPropertyA { get; set; }
public static int MyPublicStaticPropertyB { get; set; }
public int MyPublicPropertyA { get; set; }
public int MyPublicPropertyB { get; set; }
internal static int MyInternalStaticPropertyA { get; set; }
internal static int MyInternalStaticPropertyB { get; set; }
internal int MyInternalPropertyA { get; set; }
internal int MyInternalPropertyB { get; set; }
protected internal static int MyProtectedInternalStaticPropertyA { get; set; }
protected internal static int MyProtectedInternalStaticPropertyB { get; set; }
protected internal int MyProtectedInternalPropertyA { get; set; }
protected internal int MyProtectedInternalPropertyB { get; set; }
private protected static int MyPrivateProtectedStaticPropertyA { get; set; }
private protected static int MyPrivateProtectedStaticPropertyB { get; set; }
private protected int MyPrivateProtectedPropertyA { get; set; }
private protected int MyPrivateProtectedPropertyB { get; set; }
protected static int MyProtectedStaticPropertyA { get; set; }
protected static int MyProtectedStaticPropertyB { get; set; }
protected int MyProtectedPropertyA { get; set; }
protected int MyProtectedPropertyB { get; set; }
private static int MyPrivateStaticPropertyA { get; set; }
private static int MyPrivateStaticPropertyB { get; set; }
private int MyPrivatePropertyA { get; set; }
private int MyPrivatePropertyB { get; set; }
Expand Down Expand Up @@ -93,6 +117,7 @@ private event MyPrivateDelegateB MyPrivateEventB { add { } remove { } }
private int this[int a, int b, int c, int d, int e, int f] { get => 0; set { } }
// Constructors
static MyClass() { }
public MyClass() { }
internal MyClass(int a, int b) { }
protected internal MyClass(int a) { }
Expand All @@ -104,16 +129,28 @@ private MyClass(int a, int b, int c, int d, int e) { }
~MyClass() { }
// Methods
public static void MyPublicStaticMethodA() { }
public static void MyPublicStaticMethodB() { }
public void MyPublicMethodA() { }
public void MyPublicMethodB() { }
internal static void MyInternalStaticMethodA() { }
internal static void MyInternalStaticMethodB() { }
internal void MyInternalMethodA() { }
internal void MyInternalMethodB() { }
protected internal static void MyProtectedInternalStaticMethodA() { }
protected internal static void MyProtectedInternalStaticMethodB() { }
protected internal void MyProtectedInternalMethodA() { }
protected internal void MyProtectedInternalMethodB() { }
private protected static void MyPrivateProtectedStaticMethodA() { }
private protected static void MyPrivateProtectedStaticMethodB() { }
private protected void MyPrivateProtectedMethodA() { }
private protected void MyPrivateProtectedMethodB() { }
protected static void MyProtectedStaticMethodA() { }
protected static void MyProtectedStaticMethodB() { }
protected void MyProtectedMethodA() { }
protected void MyProtectedMethodB() { }
private static void MyPrivateStaticMethodA() { }
private static void MyPrivateStaticMethodB() { }
private void MyPrivateMethodA() { }
private void MyPrivateMethodB() { }
}";
Expand Down Expand Up @@ -310,4 +347,38 @@ public async Task Analyzer_EmptyContent_NoDiagnostics()

await VerifyCS.VerifyAnalyzerAsync(test);
}

[TestMethod]
public void GetAccessibilityModifierOrder_NullMember_ThrowsArgumentNullException() =>

// Act and assert
Assert.ThrowsException<ArgumentNullException>(() => MemberOrderAnalyzer.GetAccessibilityModifierOrder(null!));

[TestMethod]
public void GetMemberCategoryOrder_NullMember_ThrowsArgumentNullException() =>

// Act and assert
Assert.ThrowsException<ArgumentNullException>(() => MemberOrderAnalyzer.GetMemberCategoryOrder(null!));

[TestMethod]
public void GetMemberName_NullMember_ThrowsArgumentNullException() =>

// Act and assert
Assert.ThrowsException<ArgumentNullException>(() => MemberOrderAnalyzer.GetMemberName(null!));

[TestMethod]
public void GetStaticOrder_NullMember_ThrowsArgumentNullException() =>

// Act and assert
Assert.ThrowsException<ArgumentNullException>(() => MemberOrderAnalyzer.GetStaticOrder(null!));

[TestMethod]
public void Initialize_NullContext_ThrowsArgumentNullException()
{
// Arrange
MemberOrderAnalyzer analyzer = new();

// Act and assert
Assert.ThrowsException<ArgumentNullException>(() => analyzer.Initialize(null!));
}
}

0 comments on commit 3b80a7b

Please sign in to comment.