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

377 lines
15 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.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;
}
//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));
var code = BuildAccessorsForType(classSymbol, compilation, processedTypes);
//System.Diagnostics.Debug.WriteLine($"Generated code for {classSymbol.Name}:\n{code}"); // 调试输出
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")
//{
//}
}
// 生成工厂注册代码
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="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();
code.AppendLine($"public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>");
code.AppendLine("{");
foreach (var prop in GetAllPropertiesInHierarchy(classSymbol))
{
if (prop.IsIndexer) continue;
GeneratePropertyAccessorsForType(prop, code, compilation, processedTypes);
}
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)}
// }}";
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)
{
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>
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>
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>
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!;
}
}
""";
}
}
}