1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System.Collections.Generic; 9 using System.Collections.Immutable; 10 using System.Linq; 11 using Microsoft.CodeAnalysis; 12 using Microsoft.CodeAnalysis.CSharp; 13 using Microsoft.CodeAnalysis.CSharp.Syntax; 14 using Microsoft.CodeAnalysis.Diagnostics; 15 16 namespace Antmicro.Renode.CustomAnalyzers 17 { 18 [DiagnosticAnalyzer(LanguageNames.CSharp)] 19 public class RenodeClassMembersOrderAnalyzer : DiagnosticAnalyzer 20 { 21 public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => 22 ImmutableArray.Create(ClassMembersOrderRule); 23 Initialize(AnalysisContext context)24 public override void Initialize(AnalysisContext context) 25 { 26 context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); 27 context.EnableConcurrentExecution(); 28 context.RegisterSyntaxTreeAction(CheckClassMembersOrder); 29 } 30 CheckClassMembersOrder(SyntaxTreeAnalysisContext context)31 private void CheckClassMembersOrder(SyntaxTreeAnalysisContext context) 32 { 33 var classes = context 34 .Tree 35 .GetRoot() 36 .DescendantNodes() 37 .Where(node => node.IsKind(SyntaxKind.ClassDeclaration)); 38 39 foreach(var cls in classes) 40 { 41 var declarations = cls 42 .ChildNodes() 43 .OfType<MemberDeclarationSyntax>(); 44 45 var currentMember = ClassMemberOrder.StaticConstructor; 46 47 foreach(var declaration in declarations) 48 { 49 if(TryGetClassMemberOrder(declaration, out var nextMember)) 50 { 51 if(nextMember < currentMember) 52 { 53 var diagnostic = Diagnostic.Create( 54 ClassMembersOrderRule, 55 declaration.GetLocation(), 56 new object[] {currentMember, nextMember} 57 ); 58 context.ReportDiagnostic(diagnostic); 59 } 60 currentMember = nextMember; 61 } 62 } 63 } 64 } 65 TryGetClassMemberOrder(MemberDeclarationSyntax node, out ClassMemberOrder classMemberOrder)66 private static bool TryGetClassMemberOrder(MemberDeclarationSyntax node, out ClassMemberOrder classMemberOrder) 67 { 68 var modifiers = node 69 .Modifiers 70 .Aggregate("", (acc, val) => $"{acc}_{val}"); 71 var id = $"{modifiers}_{node.Kind()}"; 72 73 if(ClassMembersOrderMap.TryGetValue(id, out classMemberOrder)) 74 { 75 return true; 76 } 77 return false; 78 } 79 80 private static readonly DiagnosticDescriptor ClassMembersOrderRule = new DiagnosticDescriptor( 81 "renode_class_members_order", 82 "Class Members Order", 83 "Wrong Class Members Order: {1} should be placed before {0}", 84 "Design", 85 DiagnosticSeverity.Warning, 86 isEnabledByDefault: false, 87 description: "Checks if class members order is preserved." 88 ); 89 90 91 #pragma warning disable SA1509 92 // SA1509 - No space before opening braces 93 private static readonly Dictionary<string, ClassMemberOrder> ClassMembersOrderMap = new Dictionary<string, ClassMemberOrder> 94 { 95 {"_static_ConstructorDeclaration", ClassMemberOrder.StaticConstructor}, 96 {"_public_static_MethodDeclaration", ClassMemberOrder.PublicStaticMethod}, 97 {"_public_static_PropertyDeclaration", ClassMemberOrder.PublicStaticProperty}, 98 {"_public_static_explicit_OperatorDeclaration", ClassMemberOrder.PublicStaticExplicitOperator}, 99 {"_public_static_implicit_OperatorDeclaration", ClassMemberOrder.PublicStaticImplicitOperator}, 100 {"_public_static_FieldDeclaration", ClassMemberOrder.PublicStaticField}, 101 {"_public_ConstructorDeclaration", ClassMemberOrder.PublicConstructor}, 102 {"_public_MethodDeclaration", ClassMemberOrder.PublicMethod}, 103 {"_public_abstract_MethodDeclaration", ClassMemberOrder.PublicAbstractMethod}, 104 {"_public_PropertyDeclaration", ClassMemberOrder.PublicProperty}, 105 {"_public_EventDeclaration", ClassMemberOrder.PublicEvent}, 106 {"_public_readonly_FieldDeclaration", ClassMemberOrder.PublicReadonlyField}, 107 {"_public_ConstDeclaration", ClassMemberOrder.PublicConst}, 108 109 {"_protected_static_MethodDeclaration", ClassMemberOrder.ProtectedStaticMethod}, 110 {"_protected_static_PropertyDeclaration", ClassMemberOrder.ProtectedStaticProperty}, 111 {"_protected_static_explicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticExplicitOperator}, 112 {"_protected_static_implicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticImplicitOperator}, 113 {"_protected_static_FieldDeclaration", ClassMemberOrder.ProtectedStaticField}, 114 {"_protected_ConstructorDeclaration", ClassMemberOrder.ProtectedConstructor}, 115 {"_protected_MethodDeclaration", ClassMemberOrder.ProtectedMethod}, 116 {"_protected_PropertyDeclaration", ClassMemberOrder.ProtectedProperty}, 117 {"_protected_EventDeclaration", ClassMemberOrder.ProtectedEvent}, 118 {"_protected_ConstDeclaration", ClassMemberOrder.ProtectedConst}, 119 120 {"_private_static_MethodDeclaration", ClassMemberOrder.PrivateStaticMethod}, 121 {"_private_static_FieldDeclaration", ClassMemberOrder.PrivateStaticField}, 122 {"_private_ConstructorDeclaration", ClassMemberOrder.PrivateConstructor}, 123 {"_private_MethodDeclaration", ClassMemberOrder.PrivateMethod}, 124 {"_private_PropertyDeclaration", ClassMemberOrder.PrivateProperty}, 125 {"_private_FieldDeclaration", ClassMemberOrder.PrivateField}, 126 {"_private_ConstDeclaration", ClassMemberOrder.PrivateConst}, 127 128 {"_public_ClassDeclaration", ClassMemberOrder.PublicClass}, 129 {"_public_DelegateDeclaration", ClassMemberOrder.PublicDelegate}, 130 {"_public_EnumDeclaration", ClassMemberOrder.PublicEnum}, 131 132 {"_protected_ClassDeclaration", ClassMemberOrder.ProtectedClass}, 133 {"_protected_DelegateDeclaration", ClassMemberOrder.ProtectedDelegate}, 134 {"_protected_EnumDeclaration", ClassMemberOrder.ProtectedEnum}, 135 136 {"_private_ClassDeclaration", ClassMemberOrder.PrivateClass}, 137 {"_private_DelegateDeclaration", ClassMemberOrder.PrivateDelegate}, 138 {"_private_EnumDeclaration", ClassMemberOrder.PrivateEnum}, 139 }; 140 #pragma warning restore SA1509 141 142 // Order of fileds in this Enum is important. 143 // It specifies allowed order of class members. 144 private enum ClassMemberOrder 145 { 146 StaticConstructor, 147 PublicStaticMethod, 148 PublicStaticProperty, 149 PublicStaticExplicitOperator, 150 PublicStaticImplicitOperator, 151 PublicStaticField, 152 PublicConstructor, 153 PublicMethod, 154 PublicAbstractMethod, 155 PublicProperty, 156 PublicEvent, 157 PublicReadonlyField, 158 PublicConst, 159 ProtectedStaticMethod, 160 ProtectedStaticProperty, 161 ProtectedStaticExplicitOperator, 162 ProtectedStaticImplicitOperator, 163 ProtectedStaticField, 164 ProtectedConstructor, 165 ProtectedMethod, 166 ProtectedProperty, 167 ProtectedEvent, 168 ProtectedConst, 169 PrivateStaticMethod, 170 PrivateStaticField, 171 PrivateConstructor, 172 PrivateMethod, 173 PrivateProperty, 174 PrivateField, 175 PrivateConst, 176 PublicClass, 177 PublicDelegate, 178 PublicEnum, 179 ProtectedClass, 180 ProtectedDelegate, 181 ProtectedEnum, 182 PrivateClass, 183 PrivateDelegate, 184 PrivateEnum, 185 } 186 } 187 } 188