// // 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 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(); 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 { {"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 ClassMembersOrderMap = new Dictionary { {"_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, } } }