JiShe.CollectBus/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs

377 lines
15 KiB
C#
Raw Normal View History

2025-04-28 14:07:51 +08:00
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace JiShe.CollectBus.IncrementalGenerator
{
/// <summary>
/// 复杂类型源生成器
/// </summary>
[Generator(LanguageNames.CSharp)]
2025-04-29 09:14:58 +08:00
public class ComplexTypeSourceAnalyzers : IIncrementalGenerator
2025-04-28 14:07:51 +08:00
{
2025-04-29 10:02:10 +08:00
private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute";
private const string SourceEntityAccessorFactoryNamespace = "JiShe.CollectBus.Analyzers.Shared";
2025-04-28 14:07:51 +08:00
public void Initialize(IncrementalGeneratorInitializationContext context)
{
2025-04-28 22:35:19 +08:00
//Debugger.Launch();
2025-04-28 14:07:51 +08:00
// 步骤1筛选带有 [SourceAnalyzers] 的类
2025-04-28 14:07:51 +08:00
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));
2025-04-28 14:07:51 +08:00
}
private static bool IsClassWithAttribute(SyntaxNode node) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0;
2025-04-28 16:37:31 +08:00
2025-04-28 22:35:19 +08:00
private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context)
2025-04-28 14:07:51 +08:00
{
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 &&
2025-04-28 16:37:31 +08:00
SymbolEqualityComparer.Default.Equals(ctor.ContainingType, attributeType))
2025-04-28 14:07:51 +08:00
{
return classDecl;
}
}
return null;
}
2025-04-28 17:45:00 +08:00
/// <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>
2025-04-28 14:07:51 +08:00
private static void GenerateCode(
Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
var processedTypes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default);
2025-04-28 16:37:31 +08:00
if (!classes.Any())
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor("GEN002", "没有目标类",
"没有找到SourceAnalyzers标记的类", "Debug", DiagnosticSeverity.Warning, true),
2025-04-28 16:37:31 +08:00
Location.None));
}
2025-04-28 14:07:51 +08:00
foreach (var classDecl in classes.Distinct())
{
var model = compilation.GetSemanticModel(classDecl.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
2025-04-28 16:37:31 +08:00
if (classSymbol == null || !processedTypes.Add(classSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor("GEN003", "无效符号",
$"类名称为{classDecl.Identifier.Text} 符号为空", "Error", DiagnosticSeverity.Error, true),
2025-04-28 16:37:31 +08:00
Location.None));
continue;
}
2025-04-28 14:07:51 +08:00
//context.ReportDiagnostic(Diagnostic.Create(
//new DiagnosticDescriptor(
// "PA001",
// "Generated Accessors",
// $"Generating accessors for {classSymbol.Name}",
// "Performance",
// DiagnosticSeverity.Info,
// true),
//Location.None));
//// 新增:输出继承链信息
//context.ReportDiagnostic(Diagnostic.Create(
// new DiagnosticDescriptor("HIERARCHY", "Class Hierarchy",
// $"Processing class: {classSymbol.Name}, BaseType: {classSymbol.BaseType?.Name}",
// "Debug", DiagnosticSeverity.Info, true),
// Location.None));
//context.ReportDiagnostic(Diagnostic.Create(
//new DiagnosticDescriptor("PA002", "Class Found",
//$"Processing class: {classSymbol.Name}", "Debug", DiagnosticSeverity.Warning, true),
//Location.None));
2025-04-28 14:07:51 +08:00
var code = BuildAccessorsForType(classSymbol, compilation, processedTypes);
2025-04-28 16:37:31 +08:00
//System.Diagnostics.Debug.WriteLine($"Generated code for {classSymbol.Name}:\n{code}"); // 调试输出
2025-04-28 16:37:31 +08:00
2025-04-28 22:35:19 +08:00
context.AddSource($"{classSymbol.Name}Extension.g.cs", code);
var code3 = BuildAccessorsForType2(classSymbol, compilation, processedTypes);
context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3);
//code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
//if (classSymbol.ContainingNamespace.ToDisplayString() == "JiShe.CollectBus.IoTDB")
//{
//}
2025-04-28 14:07:51 +08:00
}
// 生成工厂注册代码
context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode());
2025-04-28 14:07:51 +08:00
}
/// <summary>
/// 构建类的属性访问器代码
/// </summary>
/// <param name="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
2025-04-28 14:07:51 +08:00
private static string BuildAccessorsForType(
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
2025-04-28 14:07:51 +08:00
{
var code = new StringBuilder();
2025-04-28 16:37:31 +08:00
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
code.AppendLine("// Generated code for " + classSymbol.Name);
2025-04-28 14:07:51 +08:00
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="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
private static string BuildAccessorsForType2(
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();
2025-04-28 17:45:00 +08:00
code.AppendLine($"public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>");
code.AppendLine("{");
2025-04-28 17:45:00 +08:00
foreach (var prop in GetAllPropertiesInHierarchy(classSymbol))
2025-04-28 14:07:51 +08:00
{
if (prop.IsIndexer) continue;
GeneratePropertyAccessorsForType(prop, code, compilation, processedTypes);
2025-04-28 14:07:51 +08:00
}
code.AppendLine("}");
//var code3 = $@"
// public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>
// {{
// public object GetPropertyValue({classSymbol.Name} entity, string propName) => propName switch
// {{
// {GeneratePropertyCases(classSymbol)}
// _ => throw new ArgumentException(""无效属性名"")
// }};
// {GenerateTupleSupport(classSymbol)}
// }}";
2025-04-28 14:07:51 +08:00
return code.ToString();
}
/// <summary>
/// 获取泛型参数
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
2025-04-28 16:37:31 +08:00
public static string GetGenericParams(INamedTypeSymbol symbol)
{
if (!symbol.IsGenericType) return "";
var parameters = symbol.TypeParameters.Select(t => t.Name);
return $"<{string.Join(", ", parameters)}>";
}
2025-04-28 14:07:51 +08:00
/// <summary>
/// 生成属性访问器代码
/// </summary>
/// <param name="prop"></param>
/// <param name="code"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
private static void GeneratePropertyAccessorsForType(
2025-04-28 14:07:51 +08:00
IPropertySymbol prop,
StringBuilder code,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
// 安全类型转换
2025-04-28 14:07:51 +08:00
if (prop.Type is not ITypeSymbol propType) return;
code.AppendLine($" // Processing property: {prop.Name}");
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
GenerateTupleAccessors(prop, tupleType, code);
}
else if (propType is INamedTypeSymbol namedType)
{
GenerateStandardAccessors(prop, namedType, code);
ProcessNestedType(namedType, compilation, processedTypes);
}
}
/// <summary>
/// 处理元组类型
/// </summary>
/// <param name="prop"></param>
/// <param name="tupleType"></param>
/// <param name="code"></param>
2025-04-28 14:07:51 +08:00
private static void GenerateTupleAccessors(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>
2025-04-28 14:07:51 +08:00
private static void GenerateStandardAccessors(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>
2025-04-28 14:07:51 +08:00
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>
2025-04-28 14:07:51 +08:00
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!;
}
}
""";
}
2025-04-28 14:07:51 +08:00
}
}