Przeglądaj źródła

添加分析器规则 (#555)

* 添加消息禁止实体字段分析器

* 更新实体成员分析器 实体类禁止添加含有实体类参数的泛型字段 可以使用EntityRef

* 添加DisableNewAttribute 标记在类或结构体上时 该类及其子类就禁止使用new构造对象
susices 2 lat temu
rodzic
commit
59a86a1633

+ 47 - 0
Share/Analyzer/Analyzer/DiableNewAnalyzer.cs

@@ -0,0 +1,47 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace ET.Analyzer
+{
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public class DiableNewAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>ImmutableArray.Create(DisableNewAnalyzerRule.Rule);
+        
+        public override void Initialize(AnalysisContext context)
+        {
+            if (!AnalyzerGlobalSetting.EnableAnalyzer)
+            {
+                return;
+            }
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+            context.EnableConcurrentExecution();
+            
+            context.RegisterSyntaxNodeAction(this.AnalyzeObjectCreationExpression, SyntaxKind.ObjectCreationExpression);
+        }
+
+        private void AnalyzeObjectCreationExpression(SyntaxNodeAnalysisContext context)
+        {
+            if (context.Node is not ObjectCreationExpressionSyntax objectCreationExpressionSyntax)
+            {
+                return;
+            }
+            
+            var typeSymbol = context.SemanticModel.GetSymbolInfo(objectCreationExpressionSyntax.Type).Symbol as ITypeSymbol;
+            if (typeSymbol==null)
+            {
+                return;
+            }
+            if (typeSymbol.HasAttributeInTypeAndBaseTyes(Definition.DisableNewAttribute))
+            {
+                Diagnostic diagnostic = Diagnostic.Create(DisableNewAnalyzerRule.Rule, objectCreationExpressionSyntax?.GetLocation(),typeSymbol);
+                context.ReportDiagnostic(diagnostic);
+            }
+        }
+    }
+}
+

+ 60 - 1
Share/Analyzer/Analyzer/EntityMemberDeclarationAnalyzer.cs

@@ -114,7 +114,27 @@ namespace ET.Analyzer
                 {
                     continue;
                 }
-                if (fieldSymbol.Type.ToString()is Definition.EntityType or Definition.LSEntityType || fieldSymbol.Type.BaseType?.ToString()is Definition.EntityType or Definition.LSEntityType)
+
+                if (fieldSymbol.Type is not INamedTypeSymbol namedTypeSymbol2)
+                {
+                    continue;
+                }
+
+                // 字段类型是否是实体类
+                if (namedTypeSymbol2.IsETEntity())
+                {
+                    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);
+                    continue;
+                }
+
+                // 字段类型是否是含实体类参数的泛型类
+                if (namedTypeSymbol2.IsGenericType&&GenericTypeHasEntityTypeArgs(namedTypeSymbol2))
                 {
                     var syntaxReference = fieldSymbol.DeclaringSyntaxReferences.FirstOrDefault();
                     if (syntaxReference==null)
@@ -226,6 +246,45 @@ namespace ET.Analyzer
                 context.ReportDiagnostic(diagnostic);
             }
         }
+
+
+        /// <summary>
+        /// 泛型类 是否含有的实体类型参数 
+        /// 对于嵌套泛型参数 递归判断
+        /// </summary>
+        private bool GenericTypeHasEntityTypeArgs(INamedTypeSymbol namedTypeSymbol)
+        {
+            if (namedTypeSymbol.IsEntityRefOrEntityWeakRef())
+            {
+                return false;
+            }
+            
+            var typeArgs = namedTypeSymbol.TypeArguments;
+            foreach (var typeSymbol in typeArgs)
+            {
+                if (typeSymbol is not INamedTypeSymbol namedTypeSymbol2)
+                {
+                    break;
+                }
+
+                if (namedTypeSymbol2.IsGenericType)
+                {
+                    if (GenericTypeHasEntityTypeArgs(namedTypeSymbol2))
+                    {
+                        return true;
+                    }
+                }
+                else
+                {
+                    if (namedTypeSymbol2.IsETEntity())
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
     }
 }
 

+ 78 - 0
Share/Analyzer/Analyzer/NetMessageAnalyzer.cs

@@ -0,0 +1,78 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace ET.Analyzer
+{
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public class NetMessageAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>ImmutableArray.Create(NetMessageAnalyzerRule.Rule);
+        public override void Initialize(AnalysisContext context)
+        {
+            if (!AnalyzerGlobalSetting.EnableAnalyzer)
+            {
+                return;
+            }
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+            context.EnableConcurrentExecution();
+            context.RegisterSyntaxNodeAction(this.AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration);
+        }
+        
+        private void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context)
+        {
+            if (!AnalyzerHelper.IsAssemblyNeedAnalyze(context.Compilation.AssemblyName, AnalyzeAssembly.AllModel))
+            {
+                return;
+            }
+
+            if (context.Node is not ClassDeclarationSyntax classDeclarationSyntax)
+            {
+                return;
+            }
+
+            var namedTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
+            if (namedTypeSymbol==null)
+            {
+                return;
+            }
+
+            // 筛选出继承IMessage接口的类
+            if (!namedTypeSymbol.HasInterface(Definition.IMessageInterface))
+            {
+                return;
+            }
+
+            foreach (var member in namedTypeSymbol.GetMembers())
+            {
+                ITypeSymbol? memberType = null;
+                
+                if (member is IFieldSymbol fieldSymbol)
+                {
+                    memberType = fieldSymbol.Type;
+                }else if (member is IPropertySymbol propertySymbol)
+                {
+                    memberType = propertySymbol.Type;
+                }
+
+                if (memberType==null)
+                {
+                    continue;
+                }
+
+                if (!memberType.IsETEntity())
+                {
+                    continue;
+                }
+
+                var memberSyntax = member.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();
+                Diagnostic diagnostic = Diagnostic.Create(NetMessageAnalyzerRule.Rule, memberSyntax?.GetLocation(),namedTypeSymbol.Name, member.Name);
+                context.ReportDiagnostic(diagnostic);
+            }
+        }
+    }
+}
+

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

@@ -86,6 +86,14 @@
         public const string LSUpdateMethod = "LSUpdate";
 
         public const string ETLog = "ET.Log";
+
+        public const string IMessageInterface = "ET.IMessage";
+
+        public const string EntityRefType = "EntityRef";
+        
+        public const string EntityWeakRefType = "EntityWeakRef";
+
+        public const string DisableNewAttribute = "ET.DisableNewAttribute";
     }
 }
 

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

@@ -60,5 +60,9 @@
         public const string EntityComponentChildAnalyzerRuleId = "ET0028";
 
         public const string EntityCannotDeclareGenericTypeRuleId = "ET0029";
+
+        public const string NetMessageAnalyzerRuleId = "ET0030";
+
+        public const string DisableNewAnalyzerRuleId = "ET0031";
     }
 }

+ 38 - 1
Share/Analyzer/Config/DiagnosticRules.cs

@@ -256,7 +256,7 @@ namespace ET.Analyzer
     {
         private const string Title = "实体类禁止声明实体字段";
 
-        private const string MessageFormat = "实体类: {0} 不能在类内部声明实体字段: {1}";
+        private const string MessageFormat = "实体类: {0} 不能在类内部声明实体或含有实体类参数的泛型类字段: {1} 请使用EntityRef代替";
 
         private const string Description = "实体类禁止声明实体字段.";
 
@@ -431,4 +431,41 @@ namespace ET.Analyzer
                     true,
                     Description);
     }
+    
+    
+    public static class NetMessageAnalyzerRule
+    {
+        private const string Title = "消息类禁止声明实体字段";
+
+        private const string MessageFormat = "消息类: {0} 禁止声明实体字段: {1}";
+
+        private const string Description = "消息类禁止声明实体字段.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.NetMessageAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.All,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
+    
+    public static class DisableNewAnalyzerRule
+    {
+        private const string Title = "含有DisableNew标记的类禁止使用new构造对象";
+
+        private const string MessageFormat = "禁止使用new构造{0}类型的对象";
+
+        private const string Description = "含有DisableNew标记的类禁止使用new构造对象.";
+
+        public static readonly DiagnosticDescriptor Rule =
+                new DiagnosticDescriptor(DiagnosticIds.DisableNewAnalyzerRuleId,
+                    Title,
+                    MessageFormat,
+                    DiagnosticCategories.All,
+                    DiagnosticSeverity.Error,
+                    true,
+                    Description);
+    }
 }

+ 59 - 2
Share/Analyzer/Extension/AnalyzerHelper.cs

@@ -83,9 +83,9 @@ namespace ET.Analyzer
         /// <summary>
         ///     INamedTypeSymbol 是否有指定的Attribute
         /// </summary>
-        public static bool HasAttribute(this INamedTypeSymbol namedTypeSymbol, string AttributeName)
+        public static bool HasAttribute(this ITypeSymbol typeSymbol, string AttributeName)
         {
-            foreach (AttributeData? attributeData in namedTypeSymbol.GetAttributes())
+            foreach (AttributeData? attributeData in typeSymbol.GetAttributes())
             {
                 if (attributeData.AttributeClass?.ToString() == AttributeName)
                 {
@@ -96,6 +96,33 @@ namespace ET.Analyzer
             return false;
         }
 
+        public static bool HasAttributeInTypeAndBaseTyes(this ITypeSymbol typeSymbol, string AttributeName)
+        {
+            if (typeSymbol.HasAttribute(AttributeName))
+            {
+                return true;
+            }
+
+            foreach (var baseType in typeSymbol.BaseTypes())
+            {
+                if (baseType.HasAttribute(AttributeName))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public static IEnumerable<ITypeSymbol> BaseTypes(this ITypeSymbol typeSymbol)
+        {
+            ITypeSymbol? baseType = typeSymbol.BaseType;
+            while (baseType!=null)
+            {
+                yield return baseType;
+                baseType = baseType.BaseType;
+            }
+        }
+
         /// <summary>
         ///     INamedTypeSymbol 是否有指定的基类Attribute
         /// </summary>
@@ -536,5 +563,35 @@ namespace ET.Analyzer
             }
             return false;
         }
+
+        /// <summary>
+        /// 类型symbol是否是实体类 包含 enity及其子类 lsentity及其子类
+        /// </summary>
+        public static bool IsETEntity(this ITypeSymbol typeSymbol)
+        {
+            string typeName = typeSymbol.ToString();
+            string? baseType = typeSymbol.BaseType?.ToString();
+            return typeName == Definition.EntityType || baseType == Definition.EntityType || baseType == Definition.LSEntityType;
+        }
+
+        /// <summary>
+        /// 类型symbol是否是EntiyRef 或EntityWeakRef类
+        /// </summary>
+        public static bool IsEntityRefOrEntityWeakRef(this ITypeSymbol typeSymbol)
+        {
+            if (typeSymbol is not INamedTypeSymbol namedTypeSymbol)
+            {
+                return false;
+            }
+            
+
+            if (!namedTypeSymbol.IsGenericType)
+            {
+                return false;
+            }
+
+            string typeName = namedTypeSymbol.Name;
+            return typeName is Definition.EntityRefType or Definition.EntityWeakRefType;
+        }
     }
 }

+ 14 - 0
Unity/Assets/Scripts/Core/Analyzer/DisableNewAttribute.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace ET
+{
+    /// <summary>
+    /// 添加该标记的类或结构体禁止使用new关键字构造对象
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,Inherited = true)]
+    public class DisableNewAttribute : Attribute
+    {
+        
+    }
+}
+

+ 11 - 0
Unity/Assets/Scripts/Core/Analyzer/DisableNewAttribute.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e75d73a390a0bcc469c6d5f9f8449e0b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
Unity/Assets/Scripts/Core/Object/MessageObject.cs

@@ -4,6 +4,7 @@ using MongoDB.Bson.Serialization.Attributes;
 
 namespace ET
 {
+    [DisableNew]
     public abstract class MessageObject: ProtoObject, IMessage, IDisposable, IPool
     {
         public virtual void Dispose()