EntityComponentAnalyzer.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System;
  2. using System.Collections.Immutable;
  3. using System.Linq;
  4. using Microsoft.CodeAnalysis;
  5. using Microsoft.CodeAnalysis.CSharp;
  6. using Microsoft.CodeAnalysis.CSharp.Syntax;
  7. using Microsoft.CodeAnalysis.Diagnostics;
  8. namespace ET.Analyzer
  9. {
  10. [DiagnosticAnalyzer(LanguageNames.CSharp)]
  11. public class EntityComponentAnalyzer:DiagnosticAnalyzer
  12. {
  13. private const string Title = "实体类添加或获取组件类型错误";
  14. private const string MessageFormat = "组件类型: {0} 不允许作为实体: {1} 的组件类型! 若要允许该类型作为参数,请使用ComponentOfAttribute对组件类标记父级实体类型";
  15. private const string Description = "实体类添加或获取组件类型错误.";
  16. private static readonly string[] ComponentMethod = {"AddComponent","GetComponent"};
  17. private const string EntityType = "ET.Entity";
  18. private static readonly DiagnosticDescriptor Rule =
  19. new DiagnosticDescriptor(DiagnosticIds.EntityComponentAnalyzerRuleId,
  20. Title,
  21. MessageFormat,
  22. DiagnosticCategories.Hotfix,
  23. DiagnosticSeverity.Error,
  24. true,
  25. Description);
  26. public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
  27. public override void Initialize(AnalysisContext context)
  28. {
  29. if (!AnalyzerGlobalSetting.EnableAnalyzer)
  30. {
  31. return;
  32. }
  33. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
  34. context.EnableConcurrentExecution();
  35. context.RegisterSyntaxNodeAction(this.AnalyzeMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression);
  36. }
  37. private void AnalyzeMemberAccessExpression(SyntaxNodeAnalysisContext context)
  38. {
  39. if (!AnalyzerHelper.IsAssemblyNeedAnalyze(context.Compilation.AssemblyName, AnalyzeAssembly.All))
  40. {
  41. return;
  42. }
  43. if (!(context.Node is MemberAccessExpressionSyntax memberAccessExpressionSyntax))
  44. {
  45. return;
  46. }
  47. // 筛选出 Component函数syntax
  48. string methodName = memberAccessExpressionSyntax.Name.Identifier.Text;
  49. if (!ComponentMethod.Contains(methodName))
  50. {
  51. return;
  52. }
  53. if (!(memberAccessExpressionSyntax?.Parent is InvocationExpressionSyntax invocationExpressionSyntax) ||
  54. !(context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax).Symbol is IMethodSymbol addComponentMethodSymbol))
  55. {
  56. return;
  57. }
  58. // 获取AComponent函数的调用者类型
  59. ITypeSymbol? parentTypeSymbol = memberAccessExpressionSyntax.GetMemberAccessSyntaxParentType(context.SemanticModel);
  60. if (parentTypeSymbol==null)
  61. {
  62. return;
  63. }
  64. // 只检查Entity的子类
  65. if (parentTypeSymbol.BaseType?.ToString()!= EntityType)
  66. {
  67. return;
  68. }
  69. // 获取 component实体类型
  70. ISymbol? componentTypeSymbol = null;
  71. // Component为泛型调用
  72. if (addComponentMethodSymbol.IsGenericMethod)
  73. {
  74. GenericNameSyntax? genericNameSyntax = memberAccessExpressionSyntax?.GetFirstChild<GenericNameSyntax>();
  75. TypeArgumentListSyntax? typeArgumentList = genericNameSyntax?.GetFirstChild<TypeArgumentListSyntax>();
  76. var componentTypeSyntax = typeArgumentList?.Arguments.First();
  77. if (componentTypeSyntax == null)
  78. {
  79. Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax?.Name.Identifier.GetLocation());
  80. context.ReportDiagnostic(diagnostic);
  81. throw new Exception("componentTypeSyntax==null");
  82. }
  83. componentTypeSymbol = context.SemanticModel.GetSymbolInfo(componentTypeSyntax).Symbol;
  84. }
  85. //Component为非泛型调用
  86. else
  87. {
  88. SyntaxNode? firstArgumentSyntax = invocationExpressionSyntax.GetFirstChild<ArgumentListSyntax>()?.GetFirstChild<ArgumentSyntax>()
  89. ?.ChildNodes().First();
  90. if (firstArgumentSyntax == null)
  91. {
  92. Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax?.Name.Identifier.GetLocation());
  93. context.ReportDiagnostic(diagnostic);
  94. return;
  95. }
  96. // 参数为typeOf时 提取Type类型
  97. if (firstArgumentSyntax is TypeOfExpressionSyntax typeOfExpressionSyntax)
  98. {
  99. firstArgumentSyntax = typeOfExpressionSyntax.Type;
  100. }
  101. ISymbol? firstArgumentSymbol = context.SemanticModel.GetSymbolInfo(firstArgumentSyntax).Symbol;
  102. if (firstArgumentSymbol is ILocalSymbol childLocalSymbol)
  103. {
  104. componentTypeSymbol = childLocalSymbol.Type;
  105. }
  106. else if (firstArgumentSymbol is IParameterSymbol childParamaterSymbol)
  107. {
  108. componentTypeSymbol = childParamaterSymbol.Type;
  109. }
  110. else if (firstArgumentSymbol is IMethodSymbol methodSymbol)
  111. {
  112. componentTypeSymbol = methodSymbol.ReturnType;
  113. }
  114. else if (firstArgumentSymbol is IFieldSymbol fieldSymbol)
  115. {
  116. componentTypeSymbol = fieldSymbol.Type;
  117. }
  118. else if (firstArgumentSymbol is IPropertySymbol propertySymbol)
  119. {
  120. componentTypeSymbol = propertySymbol.Type;
  121. }else if (firstArgumentSymbol is INamedTypeSymbol namedTypeSymbol)
  122. {
  123. componentTypeSymbol = namedTypeSymbol;
  124. }else if (firstArgumentSymbol is ITypeParameterSymbol)
  125. {
  126. // 忽略typeof(T)参数类型
  127. return;
  128. }
  129. else if (firstArgumentSymbol != null)
  130. {
  131. Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax?.Name.Identifier.GetLocation(),
  132. firstArgumentSymbol.Name, parentTypeSymbol.Name);
  133. context.ReportDiagnostic(diagnostic);
  134. return;
  135. }
  136. else
  137. {
  138. Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax?.Name.Identifier.GetLocation(),
  139. firstArgumentSyntax.GetText(), parentTypeSymbol.Name);
  140. context.ReportDiagnostic(diagnostic);
  141. return;
  142. }
  143. }
  144. if (componentTypeSymbol==null)
  145. {
  146. return;
  147. }
  148. // 忽略 component类型为泛型类型
  149. if (componentTypeSymbol is ITypeParameterSymbol typeParameterSymbol)
  150. {
  151. return;
  152. }
  153. // 忽略 Type参数
  154. if (componentTypeSymbol.ToString()=="System.Type")
  155. {
  156. return;
  157. }
  158. // 组件类型为Entity时 忽略检查
  159. if (componentTypeSymbol.ToString()== EntityType)
  160. {
  161. return;
  162. }
  163. // 判断component类型是否属于约束类型
  164. //获取component类的parentType标记数据
  165. INamedTypeSymbol? availableParentTypeSymbol = null;
  166. bool hasParentTypeAttribute = false;
  167. foreach (AttributeData? attributeData in componentTypeSymbol.GetAttributes())
  168. {
  169. if (attributeData.AttributeClass?.Name == "ComponentOfAttribute")
  170. {
  171. hasParentTypeAttribute = true;
  172. if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol typeSymbol)
  173. {
  174. availableParentTypeSymbol = typeSymbol;
  175. break;
  176. }
  177. }
  178. }
  179. if (hasParentTypeAttribute&&availableParentTypeSymbol==null)
  180. {
  181. return;
  182. }
  183. // 符合约束条件 通过检查
  184. if (availableParentTypeSymbol!=null && availableParentTypeSymbol.ToString()==parentTypeSymbol.ToString())
  185. {
  186. return;
  187. }
  188. {
  189. Diagnostic diagnostic = Diagnostic.Create(Rule, memberAccessExpressionSyntax?.Name.Identifier.GetLocation(), componentTypeSymbol?.Name,
  190. parentTypeSymbol?.Name);
  191. context.ReportDiagnostic(diagnostic);
  192. }
  193. }
  194. }
  195. }