JiShe.CollectBus/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs
2025-05-07 10:15:45 +08:00

450 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace JiShe.CollectBus.IncrementalGenerator
{
/// <summary>
/// 复杂类型增量源生成器
/// </summary>
[Generator(LanguageNames.CSharp)]
public class ComplexTypeSourceAnalyzers : IIncrementalGenerator
{
private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute";
private const string SourceEntityAccessorFactoryNamespace = "JiShe.CollectBus.Analyzers.Shared";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//Debugger.Launch();
// 步骤1筛选带有 [SourceAnalyzers] 的类
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsClassWithAttribute(s),
transform: static (ctx, _) => GetClassDeclaration(ctx))
.Where(static c => c is not null);
// 步骤2合并编译信息
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses, (spc, source) =>
GenerateCode(source.Left, source.Right!, spc));
}
private static bool IsClassWithAttribute(SyntaxNode node) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0;
private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context)
{
var classDecl = (ClassDeclarationSyntax)context.Node;
var attributeType = context.SemanticModel.Compilation.GetTypeByMetadataName(AttributeFullName);
foreach (var attribute in classDecl.AttributeLists.SelectMany(al => al.Attributes))
{
var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol;
if (symbol is IMethodSymbol ctor &&
SymbolEqualityComparer.Default.Equals(ctor.ContainingType, attributeType))
{
return classDecl;
}
}
return null;
}
/// <summary>
/// 递归获取所有层级的属性
/// </summary>
/// <param name="classSymbol"></param>
/// <returns></returns>
private static IEnumerable<IPropertySymbol> GetAllPropertiesInHierarchy(INamedTypeSymbol classSymbol)
{
var currentSymbol = classSymbol;
while (currentSymbol != null)
{
foreach (var prop in currentSymbol.GetMembers().OfType<IPropertySymbol>())
{
yield return prop;
}
currentSymbol = currentSymbol.BaseType; // 向上遍历基类
}
}
/// <summary>
/// 生成代码
/// </summary>
/// <param name="compilation"></param>
/// <param name="classes"></param>
/// <param name="context"></param>
private static void GenerateCode(
Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
var processedTypes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default);
if (!classes.Any())
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor("GEN002", "没有目标类",
"没有找到SourceAnalyzers标记的类", "Debug", DiagnosticSeverity.Warning, true),
Location.None));
}
foreach (var classDecl in classes.Distinct())
{
var model = compilation.GetSemanticModel(classDecl.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
if (classSymbol == null || !processedTypes.Add(classSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor("GEN003", "无效符号",
$"类名称为{classDecl.Identifier.Text} 符号为空", "Error", DiagnosticSeverity.Error, true),
Location.None));
continue;
}
var code = BuildAccessorsForType(classSymbol, compilation, processedTypes);
context.AddSource($"{classSymbol.Name}Extension.g.cs", code);
var code3 = BuildAccessorsForSourceEntity(classSymbol, compilation, processedTypes);
context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3);
}
// 生成工厂注册代码
context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode());
}
/// <summary>
/// 构建类的属性访问器代码
/// </summary>
/// <param name="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
private static string BuildAccessorsForType(
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
var code = new StringBuilder();
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
code.AppendLine("// Generated code for " + classSymbol.Name);
code.AppendLine("// <auto-generated/>");
code.AppendLine("#nullable enable");
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
code.AppendLine();
code.AppendLine($"public static class {classSymbol.Name}Extension{GetGenericParams(classSymbol)}");
code.AppendLine("{");
foreach (var prop in GetAllPropertiesInHierarchy(classSymbol))
{
if (prop.IsIndexer) continue;
GeneratePropertyAccessorsForType(prop, code, compilation, processedTypes);
}
code.AppendLine("}");
return code.ToString();
}
/// <summary>
/// 获取泛型参数
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
public static string GetGenericParams(INamedTypeSymbol symbol)
{
if (!symbol.IsGenericType) return "";
var parameters = symbol.TypeParameters.Select(t => t.Name);
return $"<{string.Join(", ", parameters)}>";
}
/// <summary>
/// 生成属性访问器代码
/// </summary>
/// <param name="prop"></param>
/// <param name="code"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
private static void GeneratePropertyAccessorsForType(
IPropertySymbol prop,
StringBuilder code,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
// 安全类型转换
if (prop.Type is not ITypeSymbol propType) return;
code.AppendLine($" // Processing property: {prop.Name}");
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
GenerateTupleAccessorsForType(prop, tupleType, code);
}
else if (propType is INamedTypeSymbol namedType)
{
GenerateStandardAccessorsForType(prop, namedType, code);
ProcessNestedType(namedType, compilation, processedTypes);
}
}
/// <summary>
/// 处理元组类型
/// </summary>
/// <param name="prop"></param>
/// <param name="tupleType"></param>
/// <param name="code"></param>
private static void GenerateTupleAccessorsForType(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
{
var elements = tupleType.TupleElements;
var parentType = prop.ContainingType.ToDisplayString();
for (int i = 0; i < elements.Length; i++)
{
var element = elements[i];
if (element.Type is not ITypeSymbol elementType) continue;
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
if (prop.SetMethod != null)
{
var assignments = elements.Select((e, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
}
}
}
/// <summary>
/// 生成标准属性的访问器
/// </summary>
/// <param name="prop"></param>
/// <param name="propType"></param>
/// <param name="code"></param>
private static void GenerateStandardAccessorsForType(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
{
var parentType = prop.ContainingType.ToDisplayString();
code.AppendLine($" public static {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
if (prop.SetMethod != null)
{
code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
}
}
/// <summary>
/// 处理嵌套类型
/// </summary>
/// <param name="typeSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
private static void ProcessNestedType(ITypeSymbol typeSymbol, Compilation compilation, HashSet<ITypeSymbol> processedTypes)
{
if (typeSymbol is not INamedTypeSymbol namedType) return;
if (!ShouldProcessNestedType(namedType)) return;
if (!processedTypes.Add(namedType)) return;
var code = BuildAccessorsForType(namedType, compilation, processedTypes);
}
/// <summary>
/// 处理嵌套类型
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
private static bool ShouldProcessNestedType(INamedTypeSymbol symbol)
{
return symbol.DeclaredAccessibility == Accessibility.Public &&
!symbol.IsTupleType &&
!symbol.IsAnonymousType &&
!symbol.IsImplicitlyDeclared &&
!symbol.Name.StartsWith("<");
}
/// <summary>
/// 生成扩展类工厂代码
/// </summary>
/// <returns></returns>
private static string BuildFactoryCode()
{
return """
using System;
using System.Collections.Concurrent;
namespace JiShe.CollectBus.Analyzers.Shared;
public static class SourceEntityAccessorFactory
{
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
public static ISourceEntityAccessor<T> GetAccessor<T>()
{
if (!_accessors.TryGetValue(typeof(T), out var accessor))
{
accessor = Activator.CreateInstance(
Type.GetType($"{typeof(T).Namespace}.{typeof(T).Name}Accessor")!)!;
_accessors.TryAdd(typeof(T), accessor);
}
return (ISourceEntityAccessor<T>)accessor!;
}
}
""";
}
/// <summary>
/// 构建实体类的源属性访问器代码
/// </summary>
/// <param name="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
private static string BuildAccessorsForSourceEntity(
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
var code = new StringBuilder();
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
code.AppendLine("// Generated code for " + classSymbol.Name);
code.AppendLine("// <auto-generated/>");
code.AppendLine("#nullable enable");
code.AppendLine("using System;");
code.AppendLine("using JiShe.CollectBus.Analyzers.Shared;");
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
code.AppendLine();
code.AppendLine($"public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>");
code.AppendLine("{");
GetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
SetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
code.AppendLine("}");
return code.ToString();
}
private static void GetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
code.AppendLine($" public object GetPropertyValue({classSymbol.Name} targetEntity, string propertyName)");
code.AppendLine(" {");
code.AppendLine(" return propertyName switch");
code.AppendLine(" {");
// 遍历所有属性
foreach (var property in propList)
{
// 安全类型转换
if (property.Type is not ITypeSymbol propType) continue;
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
}
else if (propType is INamedTypeSymbol namedType)
{
// 生成属性访问的
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Get{property.Name}(targetEntity),");
}
}
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
code.AppendLine(" };");
code.AppendLine(" }");
}
private static void SetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
code.AppendLine($" public void SetPropertyValue({classSymbol.Name} targetEntity, string propertyName, object value)");
code.AppendLine(" {");
code.AppendLine(" return propertyName switch");
code.AppendLine(" {");
// 遍历所有属性
foreach (var property in propList)
{
// 安全类型转换
if (property.Type is not ITypeSymbol propType) continue;
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
}
else if (propType is INamedTypeSymbol namedType)
{
// 生成属性设置赋值
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Set{property.Name}(targetEntity,({namedType.ToDisplayString()})value),");
}
}
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
code.AppendLine(" };");
code.AppendLine(" }");
}
/// <summary>
/// 处理元组类型
/// </summary>
/// <param name="prop"></param>
/// <param name="tupleType"></param>
/// <param name="code"></param>
private static void GenerateTupleAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
{
var elements = tupleType.TupleElements;
var parentType = prop.ContainingType.ToDisplayString();
for (int i = 0; i < elements.Length; i++)
{
var element = elements[i];
if (element.Type is not ITypeSymbol elementType) continue;
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
if (prop.SetMethod != null)
{
var assignments = elements.Select((e, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
}
}
}
/// <summary>
/// 生成标准属性的访问器
/// </summary>
/// <param name="prop"></param>
/// <param name="propType"></param>
/// <param name="code"></param>
private static void GenerateStandardAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
{
var parentType = prop.ContainingType.ToDisplayString();
code.AppendLine($" public object {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
if (prop.SetMethod != null)
{
code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
}
}
}
}