248 lines
12 KiB
C#
248 lines
12 KiB
C#
//
|
|
// Copyright (c) 2010-2025 Antmicro
|
|
//
|
|
// This file is licensed under the MIT License.
|
|
// Full license text is available in 'licenses/MIT.txt'.
|
|
//
|
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
|
|
namespace Antmicro.Renode.CustomAnalyzers
|
|
{
|
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
|
public class RenodeClassMembersOrderAnalyzer : DiagnosticAnalyzer
|
|
{
|
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
|
ImmutableArray.Create(ClassMembersOrderRule);
|
|
|
|
public override void Initialize(AnalysisContext context)
|
|
{
|
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
|
context.EnableConcurrentExecution();
|
|
context.RegisterSyntaxTreeAction(CheckClassMembersOrder);
|
|
}
|
|
|
|
public const string DiagnosticId = "renode_class_members_order";
|
|
|
|
private void CheckClassMembersOrder(SyntaxTreeAnalysisContext context)
|
|
{
|
|
var classes = context
|
|
.Tree
|
|
.GetRoot()
|
|
.DescendantNodes()
|
|
.Where(node => node.IsKind(SyntaxKind.ClassDeclaration));
|
|
|
|
foreach(var cls in classes)
|
|
{
|
|
var declarations = cls
|
|
.ChildNodes()
|
|
.OfType<MemberDeclarationSyntax>();
|
|
|
|
var currentMember = ClassMemberOrder.StaticConstructor;
|
|
|
|
foreach(var declaration in declarations)
|
|
{
|
|
if(TryGetClassMemberOrder(declaration, out var nextMember))
|
|
{
|
|
if(nextMember < currentMember)
|
|
{
|
|
var diagnostic = Diagnostic.Create(
|
|
ClassMembersOrderRule,
|
|
declaration.GetLocation(),
|
|
new object[] {currentMember, nextMember}
|
|
);
|
|
context.ReportDiagnostic(diagnostic);
|
|
}
|
|
currentMember = nextMember;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool TryGetClassMemberOrder(MemberDeclarationSyntax node, out ClassMemberOrder classMemberOrder)
|
|
{
|
|
var ignoredModifiers = ImmutableHashSet.Create
|
|
(
|
|
"async",
|
|
"extern",
|
|
"internal",
|
|
"override",
|
|
"partial",
|
|
"sealed",
|
|
"unsafe",
|
|
"virtual",
|
|
"volatile"
|
|
);
|
|
|
|
var modifiersOrder = new Dictionary<string, int>
|
|
{
|
|
{"public", 0},
|
|
{"protected", 0},
|
|
{"private", 0},
|
|
{"static", 1},
|
|
{"abstract", 2},
|
|
{"readonly", 3},
|
|
{"const", 3},
|
|
}.ToImmutableDictionary();
|
|
|
|
var modifiers = node
|
|
.Modifiers
|
|
.OrderBy(mod => modifiersOrder.GetValueOrDefault(mod.ToString(), -1))
|
|
.Aggregate("", (acc, val) => ignoredModifiers.Contains(val.ToString()) ? acc : $"{acc}_{val}");
|
|
var id = $"{modifiers}_{node.Kind()}";
|
|
|
|
if(ClassMembersOrderMap.TryGetValue(id, out classMemberOrder))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#pragma warning disable SA1509
|
|
// SA1509 - No space before opening braces
|
|
public static readonly Dictionary<string, ClassMemberOrder> ClassMembersOrderMap = new Dictionary<string, ClassMemberOrder>
|
|
{
|
|
{"_static_ConstructorDeclaration", ClassMemberOrder.StaticConstructor},
|
|
{"_public_static_MethodDeclaration", ClassMemberOrder.PublicStaticMethod},
|
|
{"_public_static_PropertyDeclaration", ClassMemberOrder.PublicStaticProperty},
|
|
{"_public_static_explicit_OperatorDeclaration", ClassMemberOrder.PublicStaticExplicitOperator},
|
|
{"_public_static_implicit_OperatorDeclaration", ClassMemberOrder.PublicStaticImplicitOperator},
|
|
{"_public_static_FieldDeclaration", ClassMemberOrder.PublicStaticField},
|
|
{"_public_ConstructorDeclaration", ClassMemberOrder.PublicConstructor},
|
|
{"_public_MethodDeclaration", ClassMemberOrder.PublicMethod},
|
|
{"_public_abstract_MethodDeclaration", ClassMemberOrder.PublicAbstractMethod},
|
|
{"_public_PropertyDeclaration", ClassMemberOrder.PublicProperty},
|
|
{"_public_abstract_PropertyDeclaration", ClassMemberOrder.PublicAbstractProperty},
|
|
{"_public_abstract_readonly_PropertyDeclaration", ClassMemberOrder.PublicAbstractProperty},
|
|
{"_public_EventFieldDeclaration", ClassMemberOrder.PublicEvent},
|
|
{"_public_FieldDeclaration", ClassMemberOrder.PublicField},
|
|
{"_public_readonly_FieldDeclaration", ClassMemberOrder.PublicReadonlyField},
|
|
{"_public_const_FieldDeclaration", ClassMemberOrder.PublicConst},
|
|
|
|
{"_protected_static_MethodDeclaration", ClassMemberOrder.ProtectedStaticMethod},
|
|
{"_protected_static_PropertyDeclaration", ClassMemberOrder.ProtectedStaticProperty},
|
|
{"_protected_static_explicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticExplicitOperator},
|
|
{"_protected_static_implicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticImplicitOperator},
|
|
{"_protected_static_FieldDeclaration", ClassMemberOrder.ProtectedStaticField},
|
|
{"_protected_ConstructorDeclaration", ClassMemberOrder.ProtectedConstructor},
|
|
{"_protected_MethodDeclaration", ClassMemberOrder.ProtectedMethod},
|
|
{"_protected_abstract_MethodDeclaration", ClassMemberOrder.ProtectedAbstractMethod},
|
|
{"_protected_PropertyDeclaration", ClassMemberOrder.ProtectedProperty},
|
|
{"_protected_abstract_PropertyDeclaration", ClassMemberOrder.ProtectedAbstractProperty},
|
|
{"_protected_abstract_readonly_PropertyDeclaration", ClassMemberOrder.ProtectedAbstractProperty},
|
|
{"_protected_EventFieldDeclaration", ClassMemberOrder.ProtectedEvent},
|
|
{"_protected_FieldDeclaration", ClassMemberOrder.ProtectedField},
|
|
{"_protected_readonly_FieldDeclaration", ClassMemberOrder.ProtectedReadonlyField},
|
|
{"_protected_const_FieldDeclaration", ClassMemberOrder.ProtectedConst},
|
|
|
|
{"_private_static_MethodDeclaration", ClassMemberOrder.PrivateStaticMethod},
|
|
{"_private_static_FieldDeclaration", ClassMemberOrder.PrivateStaticField},
|
|
{"_private_ConstructorDeclaration", ClassMemberOrder.PrivateConstructor},
|
|
{"_private_MethodDeclaration", ClassMemberOrder.PrivateMethod},
|
|
{"_private_abstract_MethodDeclaration", ClassMemberOrder.PrivateAbstractMethod},
|
|
{"_private_PropertyDeclaration", ClassMemberOrder.PrivateProperty},
|
|
{"_private_abstract_PropertyDeclaration", ClassMemberOrder.PrivateAbstractProperty},
|
|
{"_private_abstract_readonly_PropertyDeclaration", ClassMemberOrder.PrivateAbstractProperty},
|
|
{"_private_FieldDeclaration", ClassMemberOrder.PrivateField},
|
|
{"_private_readonly_FieldDeclaration", ClassMemberOrder.PrivateReadonlyField},
|
|
{"_private_const_FieldDeclaration", ClassMemberOrder.PrivateConst},
|
|
|
|
{"_public_ClassDeclaration", ClassMemberOrder.PublicClass},
|
|
{"_public_abstract_ClassDeclaration", ClassMemberOrder.PublicAbstractClass},
|
|
{"_public_StructDeclaration", ClassMemberOrder.PublicStruct},
|
|
{"_public_DelegateDeclaration", ClassMemberOrder.PublicDelegate},
|
|
{"_public_EnumDeclaration", ClassMemberOrder.PublicEnum},
|
|
|
|
{"_protected_ClassDeclaration", ClassMemberOrder.ProtectedClass},
|
|
{"_protected_abstract_ClassDeclaration", ClassMemberOrder.ProtectedAbstractClass},
|
|
{"_protected_StructDeclaration", ClassMemberOrder.ProtectedStruct},
|
|
{"_protected_DelegateDeclaration", ClassMemberOrder.ProtectedDelegate},
|
|
{"_protected_EnumDeclaration", ClassMemberOrder.ProtectedEnum},
|
|
|
|
{"_private_ClassDeclaration", ClassMemberOrder.PrivateClass},
|
|
{"_private_abstract_ClassDeclaration", ClassMemberOrder.PrivateAbstractClass},
|
|
{"_private_StructDeclaration", ClassMemberOrder.PrivateStruct},
|
|
{"_private_DelegateDeclaration", ClassMemberOrder.PrivateDelegate},
|
|
{"_private_EnumDeclaration", ClassMemberOrder.PrivateEnum},
|
|
};
|
|
#pragma warning restore SA1509
|
|
|
|
private static readonly DiagnosticDescriptor ClassMembersOrderRule = new DiagnosticDescriptor(
|
|
DiagnosticId,
|
|
"Class Members Order",
|
|
"Wrong Class Members Order: {1} should be placed before {0}",
|
|
"Design",
|
|
DiagnosticSeverity.Warning,
|
|
isEnabledByDefault: false,
|
|
description: "Checks if class members order is preserved."
|
|
);
|
|
|
|
// Order of fields in this Enum is important.
|
|
// It specifies allowed order of class members.
|
|
public enum ClassMemberOrder
|
|
{
|
|
StaticConstructor,
|
|
PublicStaticMethod,
|
|
PublicStaticProperty,
|
|
PublicStaticExplicitOperator,
|
|
PublicStaticImplicitOperator,
|
|
PublicStaticField,
|
|
PublicConstructor,
|
|
PublicMethod,
|
|
PublicAbstractMethod,
|
|
PublicProperty,
|
|
PublicAbstractProperty,
|
|
PublicEvent,
|
|
PublicField,
|
|
PublicReadonlyField,
|
|
PublicConst,
|
|
ProtectedStaticMethod,
|
|
ProtectedStaticProperty,
|
|
ProtectedStaticExplicitOperator,
|
|
ProtectedStaticImplicitOperator,
|
|
ProtectedStaticField,
|
|
ProtectedConstructor,
|
|
ProtectedMethod,
|
|
ProtectedAbstractMethod,
|
|
ProtectedProperty,
|
|
ProtectedAbstractProperty,
|
|
ProtectedEvent,
|
|
ProtectedField,
|
|
ProtectedReadonlyField,
|
|
ProtectedConst,
|
|
PrivateStaticMethod,
|
|
PrivateStaticField,
|
|
PrivateConstructor,
|
|
PrivateMethod,
|
|
PrivateAbstractMethod,
|
|
PrivateProperty,
|
|
PrivateAbstractProperty,
|
|
PrivateField,
|
|
PrivateReadonlyField,
|
|
PrivateConst,
|
|
PublicClass,
|
|
PublicAbstractClass,
|
|
PublicStruct,
|
|
PublicDelegate,
|
|
PublicEnum,
|
|
ProtectedClass,
|
|
ProtectedAbstractClass,
|
|
ProtectedStruct,
|
|
ProtectedDelegate,
|
|
ProtectedEnum,
|
|
PrivateClass,
|
|
PrivateAbstractClass,
|
|
PrivateStruct,
|
|
PrivateDelegate,
|
|
PrivateEnum,
|
|
}
|
|
}
|
|
}
|