Просмотр исходного кода

添加分析器 (#456)

1. 禁止实体类中声明实体类型字段
2. 禁止声明返回值为void的异步方法
3. 禁止在Server程序集中using ET.Client 命名空间 需使用时引用类全名
susices 2 лет назад
Родитель
Сommit
3349566355

+ 58 - 0
Share/Analyzer/Analyzer/AsyncMethodReturnTypeAnalyzer.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Immutable;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace ET.Analyzer
+{
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public class AsyncMethodReturnTypeAnalyzer: DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AsyncMethodReturnTypeAnalyzerRule.Rule);
+        
+        public override void Initialize(AnalysisContext context)
+        {
+            if (!AnalyzerGlobalSetting.EnableAnalyzer)
+            {
+                return;
+            }
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+            context.EnableConcurrentExecution();
+            context.RegisterSyntaxNodeAction(this.Analyzer, SyntaxKind.MethodDeclaration,SyntaxKind.LocalFunctionStatement);
+        }
+
+        private void Analyzer(SyntaxNodeAnalysisContext context)
+        {
+            if (!AnalyzerHelper.IsAssemblyNeedAnalyze(context.Compilation.AssemblyName, AnalyzeAssembly.All))
+            {
+                return;
+            }
+
+            IMethodSymbol? methodSymbol = null;
+            
+            if (context.Node is MethodDeclarationSyntax methodDeclarationSyntax)
+            {
+                methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclarationSyntax);
+            }
+            else if (context.Node is LocalFunctionStatementSyntax localFunctionStatementSyntax)
+            {
+                methodSymbol = context.SemanticModel.GetDeclaredSymbol(localFunctionStatementSyntax) as IMethodSymbol;
+
+            }
+            if (methodSymbol==null)
+            {
+                return;
+            }
+            
+            if (methodSymbol.IsAsync && methodSymbol.ReturnsVoid)
+            {
+                Diagnostic diagnostic = Diagnostic.Create(AsyncMethodReturnTypeAnalyzerRule.Rule, context.Node.GetLocation());
+                context.ReportDiagnostic(diagnostic);
+            }
+        }
+    }
+}
+

+ 39 - 0
Share/Analyzer/Analyzer/ClientClassInServerAnalyzer.cs

@@ -0,0 +1,39 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace ET.Analyzer
+{
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public class ClientClassInServerAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(ClientClassInServerAnalyzerRule.Rule);
+        
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+            context.EnableConcurrentExecution();
+            context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.UsingDirective);
+        }
+
+        private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
+        {
+            if (!AnalyzerHelper.IsAssemblyNeedAnalyze(context.Compilation.AssemblyName, AnalyzeAssembly.ServerModelHotfix))
+            {
+                return;
+            }
+            
+            var usingDirective = (UsingDirectiveSyntax)context.Node;
+            var namespaceName = usingDirective.Name.ToString();
+
+            if (namespaceName.StartsWith(Definition.ETClientNameSpace) && !context.Node.SyntaxTree.FilePath.Contains(Definition.ClientDirInServer))
+            {
+                var diagnostic = Diagnostic.Create(ClientClassInServerAnalyzerRule.Rule, usingDirective.GetLocation());
+                context.ReportDiagnostic(diagnostic);
+            }
+        }
+    }
+}
+

+ 2 - 14
Share/Analyzer/Analyzer/EntityClassDeclarationAnalyzer.cs

@@ -7,19 +7,7 @@ namespace ET.Analyzer
     [DiagnosticAnalyzer(LanguageNames.CSharp)]
     public class EntityClassDeclarationAnalyzer: DiagnosticAnalyzer
     {
-        private const string Title = "实体类限制多层继承";
-
-        private const string MessageFormat = "类: {0} 不能继承Entiy的子类 请直接继承Entity";
-
-        private const string Description = "实体类限制多层继承.";
-
-        private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticIds.EntityClassDeclarationAnalyzerRuleId,
-            Title,
-            MessageFormat,
-            DiagnosticCategories.All,
-            DiagnosticSeverity.Error, true, Description);
-
-        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(EntityClassDeclarationAnalyzerRule.Rule);
 
         public override void Initialize(AnalysisContext context)
         {
@@ -48,7 +36,7 @@ namespace ET.Analyzer
             foreach (SyntaxReference? declaringSyntaxReference in namedTypeSymbol.DeclaringSyntaxReferences)
             {
                 SyntaxNode classSyntax = declaringSyntaxReference.GetSyntax();
-                Diagnostic diagnostic = Diagnostic.Create(Rule, classSyntax.GetLocation(), namedTypeSymbol.Name, context.Compilation.AssemblyName);
+                Diagnostic diagnostic = Diagnostic.Create(EntityClassDeclarationAnalyzerRule.Rule, classSyntax.GetLocation(), namedTypeSymbol.Name, context.Compilation.AssemblyName);
                 context.ReportDiagnostic(diagnostic);
             }
         }

+ 43 - 20
Share/Analyzer/Analyzer/EntityDelegateDeclarationAnalyzer.cs → Share/Analyzer/Analyzer/EntityMemberDeclarationAnalyzer.cs

@@ -1,30 +1,15 @@
 using System;
 using System.Collections.Immutable;
+using System.Linq;
 using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
 using Microsoft.CodeAnalysis.Diagnostics;
 
 namespace ET.Analyzer
 {
     [DiagnosticAnalyzer(LanguageNames.CSharp)]
-    public class EntityDelegateDeclarationAnalyzer: DiagnosticAnalyzer
+    public class EntityMemberDeclarationAnalyzer: DiagnosticAnalyzer
     {
-        private const string Title = "实体类禁止声明委托字段或属性";
-
-        private const string MessageFormat = "实体类: {0} 不能在类内部声明委托字段或属性: {1}";
-
-        private const string Description = "实体类禁止声明委托字段或属性.";
-
-        private static readonly DiagnosticDescriptor Rule =
-                new DiagnosticDescriptor(DiagnosticIds.DelegateAnalyzerRuleId,
-                    Title,
-                    MessageFormat,
-                    DiagnosticCategories.Model,
-                    DiagnosticSeverity.Error,
-                    true,
-                    Description);
-
-        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>ImmutableArray.Create(Rule);
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>ImmutableArray.Create(EntityDelegateDeclarationAnalyzerRule.Rule,EntityFieldDeclarationInEntityAnalyzerRule.Rule);
         
         public override void Initialize(AnalysisContext context)
         {
@@ -55,6 +40,15 @@ namespace ET.Analyzer
                 return;
             }
 
+            AnalyzeDelegateMember(context, namedTypeSymbol);
+            AnalyzeEntityMember(context, namedTypeSymbol);
+        }
+
+        /// <summary>
+        /// 检查委托成员
+        /// </summary>
+        private void AnalyzeDelegateMember(SymbolAnalysisContext context,INamedTypeSymbol namedTypeSymbol)
+        {
             foreach (var member in namedTypeSymbol.GetMembers())
             {
                 
@@ -73,13 +67,42 @@ namespace ET.Analyzer
                 }
             }
             
-
             void ReportDiagnostic(ISymbol symbol,string delegateName)
             {
                 foreach (var syntaxReference in symbol.DeclaringSyntaxReferences)
                 {
                     var syntax = syntaxReference.GetSyntax();
-                    Diagnostic diagnostic = Diagnostic.Create(Rule, syntax.GetLocation(),namedTypeSymbol.Name,delegateName);
+                    Diagnostic diagnostic = Diagnostic.Create(EntityDelegateDeclarationAnalyzerRule.Rule, syntax.GetLocation(),namedTypeSymbol.Name,delegateName);
+                    context.ReportDiagnostic(diagnostic);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 检查实体成员
+        /// </summary>
+        private void AnalyzeEntityMember(SymbolAnalysisContext context, INamedTypeSymbol namedTypeSymbol)
+        {
+            foreach (var member in namedTypeSymbol.GetMembers())
+            {
+                if (member is not IFieldSymbol fieldSymbol)
+                {
+                    continue;
+                }
+
+                // 忽略静态字段 允许单例实体类
+                if (fieldSymbol.IsStatic)
+                {
+                    continue;
+                }
+                if (fieldSymbol.Type.ToString()== Definition.EntityType || fieldSymbol.Type.BaseType?.ToString()== Definition.EntityType)
+                {
+                    var syntaxReference = fieldSymbol.DeclaringSyntaxReferences.FirstOrDefault();
+                    if (syntaxReference==null)
+                    {
+                        continue;
+                    }
+                    Diagnostic diagnostic = Diagnostic.Create(EntityFieldDeclarationInEntityAnalyzerRule.Rule, syntaxReference.GetSyntax().GetLocation(),namedTypeSymbol.Name,fieldSymbol.Name);
                     context.ReportDiagnostic(diagnostic);
                 }
             }

+ 1 - 0
Share/Analyzer/Analyzer/UniqueIdAnalyzer.cs

@@ -78,6 +78,7 @@ namespace ET.Analyzer
             
             void ReportDiagnostic(IFieldSymbol fieldSymbol, int idValue, DiagnosticDescriptor rule)
             {
+                ET.Analyzer.ClientClassInServerAnalyzer analyzer = new ClientClassInServerAnalyzer();
                 foreach (var syntaxReference in fieldSymbol.DeclaringSyntaxReferences)
                 {
                     var syntax = syntaxReference.GetSyntax();

+ 6 - 2
Share/Analyzer/Config/AnalyzeAssembly.cs

@@ -42,7 +42,11 @@
             UnityCore, UnityModel, UnityHotfix, UnityModelView, UnityHotfixView, 
             UnityModelCodes, UnityModelViewCodes, UnityHotfixCodes, UnityHotfixViewCodes
         };
-        
-        
+
+        public static readonly string[] ServerModelHotfix =
+        {
+            DotNetModel,DotNetHotfix,
+        };
+
     }
 }

+ 4 - 0
Share/Analyzer/Config/Definition.cs

@@ -35,6 +35,10 @@
         public const string ETCancellationToken = "ET.ETCancellationToken";
 
         public const string ETTaskCompleteTask = "ETTask.CompletedTask";
+
+        public const string ETClientNameSpace = "ET.Client";
+
+        public const string ClientDirInServer = @"Unity\Assets\Scripts\Codes\Hotfix\Client\";
     }
 }
 

+ 6 - 0
Share/Analyzer/Config/DiagnosticIds.cs

@@ -40,5 +40,11 @@
 
         public const string ExpressionWithCancelTokenParamAnalyzerRuleId = "ET0019";
 
+        public const string EntityFieldDeclarationInEntityAnalyzerRuleId = "ET0020";
+
+        public const string AsyncMethodReturnTypeAnalyzerRuleId = "ET0021";
+
+        public const string ClientClassInServerAnalyzerRuleId = "ET0022";
+
     }
 }

+ 87 - 0
Share/Analyzer/Config/DiagnosticRules.cs

@@ -218,4 +218,91 @@ namespace ET.Analyzer
                     true,
                     Description);
     }
+
+    public static class EntityClassDeclarationAnalyzerRule
+    {
+        private const string Title = "实体类限制多层继承";
+
+        private const string MessageFormat = "类: {0} 不能继承Entiy的子类 请直接继承Entity";
+
+        private const string Description = "实体类限制多层继承.";
+
+        public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticIds.EntityClassDeclarationAnalyzerRuleId,
+            Title,
+            MessageFormat,
+            DiagnosticCategories.All,
+            DiagnosticSeverity.Error, true, Description);
+    }
+
+    public static class EntityDelegateDeclarationAnalyzerRule
+    {
+        private const string Title = "实体类禁止声明委托字段或属性";
+
+        private const string MessageFormat = "实体类: {0} 不能在类内部声明委托字段或属性: {1}";
+
+        private const string Description = "实体类禁止声明委托字段或属性.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.DelegateAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.Model,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
+
+    public static class EntityFieldDeclarationInEntityAnalyzerRule
+    {
+        private const string Title = "实体类禁止声明实体字段";
+
+        private const string MessageFormat = "实体类: {0} 不能在类内部声明实体字段: {1}";
+
+        private const string Description = "实体类禁止声明实体字段.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.EntityFieldDeclarationInEntityAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.Model,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
+
+    public static class AsyncMethodReturnTypeAnalyzerRule
+    {
+        private const string Title = "禁止声明返回值为void的异步方法";
+
+        private const string MessageFormat = "禁止声明返回值为void的异步方法";
+
+        private const string Description = "禁止声明返回值为void的异步方法.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.AsyncMethodReturnTypeAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.Model,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
+
+    public static class ClientClassInServerAnalyzerRule
+    {
+        private const string Title = "禁止在Server程序集内引用ET.Client命名空间";
+
+        private const string MessageFormat = "禁止在Server程序集内引用ET.Client命名空间";
+
+        private const string Description = "禁止在Server程序集内引用ET.Client命名空间.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.ClientClassInServerAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.Model,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
 }