Skip to content

Commit

Permalink
New Rule T0014: Move static properties before instance ones (#9139)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource authored Apr 24, 2024
1 parent f47e64a commit 9e6e228
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2024 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.Rules.CSharp.Styling;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PropertyOrdering : StylingAnalyzer
{
public PropertyOrdering() : base("T0014", "Move this static property above the {0} instance ones.") { }

protected override void Initialize(SonarAnalysisContext context) =>
context.RegisterNodeAction(
ValidateMembers,
SyntaxKind.ClassDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.RecordStructDeclaration,
SyntaxKind.StructDeclaration);

private void ValidateMembers(SonarSyntaxNodeReportingContext context)
{
foreach (var visibilityGroup in ((TypeDeclarationSyntax)context.Node).Members.OfType<PropertyDeclarationSyntax>().GroupBy(x => x.ComputeOrder()))
{
ValidateMembers(context, visibilityGroup.Key, visibilityGroup);
}
}

private void ValidateMembers(SonarSyntaxNodeReportingContext context, OrderDescriptor order, IEnumerable<PropertyDeclarationSyntax> members)
{
bool hasInstance = false;
foreach (var member in members)
{
if (member.Modifiers.Any(SyntaxKind.StaticKeyword))
{
if (hasInstance)
{
context.ReportIssue(Rule, member.Identifier, order.Description);
}
}
else
{
hasInstance = true;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2024 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.CSharp.Styling.Test.Rules;

[TestClass]
public class PropertyOrderingTest
{
[TestMethod]
public void PropertyOrdering() =>
StylingVerifierBuilder.Create<PropertyOrdering>().AddPaths("PropertyOrdering.cs").Verify();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
public class AllValid
{
public static int S1 { get; }
public static int S2 => 42;
public static int S3
{
get => 42;
}
public int I1 { get; }
public int I2 => 42;
public int I3
{
get => 42;
}

internal static int InternalS { get; } // Compliant
internal int InternalI { get; }

protected static int ProtectedS { get; } // Compliant
protected int ProtectedI { get; }

private static int PrivateS { get; } // Compliant
private int PrivateI { get; }
}

public class SomeValid
{
public static int S1 => 42;
public int I1 => 42;
public int I2 => 42;
public static int S2 => 42; // Noncompliant {{Move this static property above the public instance ones.}}
// ^^
public int I3 => 42;

// Compliant, there're no other private/protected/internal instance fields above
internal static int InternalS { get; }
protected static int ProtectedS { get; }
protected internal static int ProtectedInternalS { get; }
protected private static int ProtectedPrivateS { get; }
private static int PrivateS { get; }
}

public class ProtectedInternal
{
protected internal int ProtectedI { get; }
protected static int ProtectedS { get; } // Noncompliant {{Move this static property above the protected instance ones.}}
}

public class ProtectedPrivate
{
protected private int ProtectedI { get; }
protected static int ProtectedS { get; } // Noncompliant {{Move this static property above the protected instance ones.}}
}

public class AllWrong
{
public int I1 { get; }
public int I2 => 42;
public int I3
{
get => 42;
}
internal int InternalI { get; }
protected int ProtectedI { get; }
private int PrivateI { get; }

public static int S1 { get; } // Noncompliant {{Move this static property above the public instance ones.}}
public static int S2 => 42; // Noncompliant
public static int S3 // Noncompliant
{
get => 42;
}

internal static int InternalS { get; } // Noncompliant {{Move this static property above the internal instance ones.}}
private static int PrivateS { get; } // Noncompliant {{Move this static property above the private instance ones.}}
protected static int ProtectedS { get; } // Noncompliant {{Move this static property above the protected instance ones.}}
}

public record R
{
public int I => 42;
public static int S => 42; // Noncompliant

}

public record struct RS
{
public int I => 42;
public static int S => 42; // Noncompliant
}

public struct Str
{
public int I => 42;
public static int S => 42; // Noncompliant
}

0 comments on commit 9e6e228

Please sign in to comment.