| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- 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 EntityFiledAccessAnalyzer: DiagnosticAnalyzer
- {
- private const string Title = "实体字段访问错误";
- private const string MessageFormat = "实体: {0} 字段: {1} 只能在实体类生命周期组件或友元类(含有FriendOfAttribute)中访问";
- private const string Description = "请使用实体类属性或方法访问其他实体字段.";
- private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticIds.EntityFiledAccessAnalyzerRuleId,
- Title,
- MessageFormat,
- DiagnosticCategories.Hotfix,
- DiagnosticSeverity.Error, true, Description);
- public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
- public override void Initialize(AnalysisContext context)
- {
- if (!AnalyzerGlobalSetting.EnableAnalyzer)
- {
- return;
- }
- context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
- context.EnableConcurrentExecution();
- context.RegisterSyntaxNodeAction(this.AnalyzeMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression);
- }
- private void AnalyzeMemberAccessExpression(SyntaxNodeAnalysisContext context)
- {
- if (!AnalyzerHelper.IsAssemblyNeedAnalyze(context.Compilation.AssemblyName, AnalyzeAssembly.All))
- {
- return;
- }
- if (!(context.Node is MemberAccessExpressionSyntax memberAccessExpressionSyntax))
- {
- return;
- }
- // -----筛选出实体类的字段symbol-----
- ISymbol? filedSymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpressionSyntax).Symbol;
- if (filedSymbol == null || !(filedSymbol is IFieldSymbol))
- {
- return;
- }
- if (filedSymbol.IsStatic)
- {
- return;
- }
- if (filedSymbol.ContainingType.BaseType?.ToString() != Definition.EntityType)
- {
- return;
- }
- // -----筛选出在实体类和实体System外部字段访问-----
- // 实体System包括awakeSystem updateSystem等生命周期类和 componentSystem静态方法类
- ClassDeclarationSyntax? accessFieldClassDeclaretion = memberAccessExpressionSyntax.GetParentClassDeclaration();
- if (accessFieldClassDeclaretion == null)
- {
- return;
- }
- INamedTypeSymbol? accessFieldClassSymbol = context.SemanticModel.GetDeclaredSymbol(accessFieldClassDeclaretion);
- if (accessFieldClassSymbol == null)
- {
- return;
- }
- // 实体基类忽略处理
- if (accessFieldClassSymbol.ToString() == Definition.EntityType)
- {
- return;
- }
- // 允许类内部访问字段
- if (accessFieldClassSymbol.ToString()== filedSymbol.ContainingType.ToString() )
- {
- return;
- }
-
- //判断是否在实体类生命周期System中
- if (this.CheckIsEntityLifecycleSystem(accessFieldClassSymbol, filedSymbol.ContainingType))
- {
- return;
- }
- //判断是否在实体类的友元类中
- if (this.CheckIsEntityFriendOf(accessFieldClassSymbol, filedSymbol.ContainingType))
- {
- return;
- }
- var builder = ImmutableDictionary.CreateBuilder<string, string?>();
- builder.Add("FriendOfType",filedSymbol.ContainingType.ToString());
- Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax.GetLocation(), builder.ToImmutable(),filedSymbol.ContainingType.ToString(),
- filedSymbol.Name);
- context.ReportDiagnostic(diagnostic);
- }
- private bool CheckIsEntityLifecycleSystem(INamedTypeSymbol accessFieldClassSymbol, INamedTypeSymbol entityTypeSymbol)
- {
- if (accessFieldClassSymbol.BaseType == null || !accessFieldClassSymbol.BaseType.IsGenericType)
- {
- return false;
- }
- // 判断是否含有 ObjectSystem Attribute 且继承了接口 ISystemType
- if (accessFieldClassSymbol.BaseType.HasAttribute(Definition.ObjectSystemAttribute) && accessFieldClassSymbol.HasInterface(Definition.ISystemType))
- {
- // 获取 accessFieldClassSymbol 父类的实体类型参数
- ITypeSymbol? entityTypeArgumentSymbol = accessFieldClassSymbol.BaseType.TypeArguments.FirstOrDefault();
- if (entityTypeArgumentSymbol == null)
- {
- return false;
- }
- // 判断 accessFieldClassSymbol 父类的实体类型参数是否为 entityTypeSymbol
- if (entityTypeArgumentSymbol.ToString() == entityTypeSymbol.ToString())
- {
- return true;
- }
- }
- return false;
- }
- private bool CheckIsEntityFriendOf(INamedTypeSymbol accessFieldTypeSymbol, INamedTypeSymbol entityTypeSymbol)
- {
- var attributes = accessFieldTypeSymbol.GetAttributes();
- foreach (AttributeData? attributeData in attributes)
- {
- if (attributeData.AttributeClass?.ToString() != Definition.FriendOfAttribute)
- {
- continue;
- }
- if (!(attributeData.ConstructorArguments[0].Value is INamedTypeSymbol namedTypeSymbol))
- {
- continue;
- }
- if (namedTypeSymbol.ToString() == entityTypeSymbol.ToString())
- {
- return true;
- }
- }
- return false;
- }
- }
- }
|