using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Apq.Cfg.SourceGenerator;
///
/// 配置节源生成器,为标记了 [CfgSection] 的类生成零反射绑定代码
///
[Generator]
public class CfgSectionGenerator : IIncrementalGenerator
{
///
/// 特性的完全限定名
///
private const string CfgSectionAttributeFullName = "Apq.Cfg.CfgSectionAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 1. 注册特性源代码(注入到用户项目)
context.RegisterPostInitializationOutput(ctx =>
{
ctx.AddSource("CfgSectionAttribute.g.cs", SourceText.From(AttributeSourceCode, Encoding.UTF8));
});
// 2. 查找所有标记了 [CfgSection] 的类
var classDeclarations = context.SyntaxProvider
.ForAttributeWithMetadataName(
CfgSectionAttributeFullName,
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, ct) => GetConfigClassInfo(ctx, ct))
.Where(static info => info is not null)
.Select(static (info, _) => info!);
// 3. 收集所有配置类信息用于生成扩展方法
var allClasses = classDeclarations.Collect();
// 4. 为每个配置类生成绑定代码
context.RegisterSourceOutput(classDeclarations, static (spc, classInfo) =>
{
var source = CodeEmitter.EmitBinderClass(classInfo);
spc.AddSource($"{classInfo.FullTypeName.Replace(".", "_")}.Binder.g.cs", SourceText.From(source, Encoding.UTF8));
});
// 5. 生成统一的扩展方法类
context.RegisterSourceOutput(allClasses, static (spc, classes) =>
{
if (classes.IsEmpty) return;
var source = CodeEmitter.EmitExtensionsClass(classes);
spc.AddSource("CfgRootGeneratedExtensions.g.cs", SourceText.From(source, Encoding.UTF8));
});
}
///
/// 从语法上下文提取配置类信息
///
private static ConfigClassInfo? GetConfigClassInfo(GeneratorAttributeSyntaxContext context, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
if (context.TargetSymbol is not INamedTypeSymbol classSymbol)
return null;
// 检查是否为 partial 类
var classDeclaration = context.TargetNode as ClassDeclarationSyntax;
if (classDeclaration == null)
return null;
bool isPartial = classDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
if (!isPartial)
{
// 非 partial 类,报告诊断(可选)
return null;
}
// 获取特性参数
var attribute = context.Attributes.FirstOrDefault();
if (attribute == null)
return null;
// 获取 SectionPath
string sectionPath = "";
if (attribute.ConstructorArguments.Length > 0 && attribute.ConstructorArguments[0].Value is string path)
{
sectionPath = path;
}
// 如果 SectionPath 为空,从类名推断
if (string.IsNullOrEmpty(sectionPath))
{
sectionPath = InferSectionPath(classSymbol.Name);
}
// 获取 GenerateExtension 属性
bool generateExtension = true;
foreach (var namedArg in attribute.NamedArguments)
{
if (namedArg.Key == "GenerateExtension" && namedArg.Value.Value is bool genExt)
{
generateExtension = genExt;
}
}
// 收集属性信息
var properties = new List();
CollectProperties(classSymbol, properties, ct);
// 获取命名空间
var namespaceName = classSymbol.ContainingNamespace.IsGlobalNamespace
? ""
: classSymbol.ContainingNamespace.ToDisplayString();
return new ConfigClassInfo(
Namespace: namespaceName,
ClassName: classSymbol.Name,
FullTypeName: classSymbol.ToDisplayString(),
SectionPath: sectionPath,
GenerateExtension: generateExtension,
Properties: properties.ToImmutableArray());
}
///
/// 从类名推断配置节路径
///
private static string InferSectionPath(string className)
{
// 移除常见后缀
string[] suffixes = { "Config", "Configuration", "Settings", "Options" };
foreach (var suffix in suffixes)
{
if (className.EndsWith(suffix, StringComparison.Ordinal) && className.Length > suffix.Length)
{
return className.Substring(0, className.Length - suffix.Length);
}
}
return className;
}
///
/// 收集类的所有可写公共属性
///
private static void CollectProperties(INamedTypeSymbol classSymbol, List properties, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
foreach (var member in classSymbol.GetMembers())
{
if (member is not IPropertySymbol prop)
continue;
// 只处理公共、可写、非索引器属性
if (prop.DeclaredAccessibility != Accessibility.Public)
continue;
if (prop.IsReadOnly || prop.IsWriteOnly)
continue;
if (prop.IsIndexer)
continue;
if (prop.IsStatic)
continue;
var propInfo = AnalyzeProperty(prop);
properties.Add(propInfo);
}
}
///
/// 分析属性类型
///
private static PropertyInfo AnalyzeProperty(IPropertySymbol prop)
{
var propType = prop.Type;
var typeKind = GetTypeKind(propType);
var elementType = GetElementType(propType);
var keyType = GetKeyType(propType);
return new PropertyInfo(
Name: prop.Name,
TypeName: propType.ToDisplayString(),
TypeKind: typeKind,
ElementTypeName: elementType?.ToDisplayString(),
KeyTypeName: keyType?.ToDisplayString(),
IsNullable: propType.NullableAnnotation == NullableAnnotation.Annotated ||
(propType is INamedTypeSymbol named && named.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T));
}
///
/// 判断类型种类
///
private static TypeKind GetTypeKind(ITypeSymbol type)
{
// 处理 Nullable
if (type is INamedTypeSymbol nullable && nullable.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
type = nullable.TypeArguments[0];
}
// 简单类型
if (IsSimpleType(type))
return TypeKind.Simple;
// 数组
if (type is IArrayTypeSymbol)
return TypeKind.Array;
// 泛型集合
if (type is INamedTypeSymbol namedType && namedType.IsGenericType)
{
var genericDef = namedType.OriginalDefinition.ToDisplayString();
// Dictionary
if (genericDef.StartsWith("System.Collections.Generic.Dictionary<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.IDictionary<", StringComparison.Ordinal))
{
return TypeKind.Dictionary;
}
// List/IList/ICollection/IEnumerable
if (genericDef.StartsWith("System.Collections.Generic.List<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.IList<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.ICollection<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.IEnumerable<", StringComparison.Ordinal))
{
return TypeKind.List;
}
// HashSet/ISet
if (genericDef.StartsWith("System.Collections.Generic.HashSet<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.ISet<", StringComparison.Ordinal))
{
return TypeKind.HashSet;
}
}
// 复杂对象
if (type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Class ||
type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Struct)
{
return TypeKind.Complex;
}
return TypeKind.Unknown;
}
///
/// 获取集合元素类型
///
private static ITypeSymbol? GetElementType(ITypeSymbol type)
{
if (type is IArrayTypeSymbol arrayType)
return arrayType.ElementType;
if (type is INamedTypeSymbol namedType && namedType.IsGenericType)
{
var args = namedType.TypeArguments;
if (args.Length >= 1)
{
var genericDef = namedType.OriginalDefinition.ToDisplayString();
if (genericDef.StartsWith("System.Collections.Generic.Dictionary<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.IDictionary<", StringComparison.Ordinal))
{
return args.Length >= 2 ? args[1] : null;
}
return args[0];
}
}
return null;
}
///
/// 获取字典键类型
///
private static ITypeSymbol? GetKeyType(ITypeSymbol type)
{
if (type is INamedTypeSymbol namedType && namedType.IsGenericType)
{
var genericDef = namedType.OriginalDefinition.ToDisplayString();
if ((genericDef.StartsWith("System.Collections.Generic.Dictionary<", StringComparison.Ordinal) ||
genericDef.StartsWith("System.Collections.Generic.IDictionary<", StringComparison.Ordinal)) &&
namedType.TypeArguments.Length >= 1)
{
return namedType.TypeArguments[0];
}
}
return null;
}
///
/// 判断是否为简单类型
///
private static bool IsSimpleType(ITypeSymbol type)
{
// 基元类型
switch (type.SpecialType)
{
case SpecialType.System_Boolean:
case SpecialType.System_Byte:
case SpecialType.System_SByte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
case SpecialType.System_Single:
case SpecialType.System_Double:
case SpecialType.System_Decimal:
case SpecialType.System_Char:
case SpecialType.System_String:
return true;
}
// 枚举
if (type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Enum)
return true;
// 其他常见简单类型
var fullName = type.ToDisplayString();
return fullName switch
{
"System.DateTime" => true,
"System.DateTimeOffset" => true,
"System.TimeSpan" => true,
"System.Guid" => true,
"System.Uri" => true,
"System.DateOnly" => true,
"System.TimeOnly" => true,
_ => false
};
}
///
/// 特性源代码(注入到用户项目)
///
private const string AttributeSourceCode = @"//
#nullable enable
namespace Apq.Cfg
{
///
/// 标记一个类为配置节,源生成器将为其生成零反射的绑定代码
///
[global::System.AttributeUsage(global::System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal sealed class CfgSectionAttribute : global::System.Attribute
{
///
/// 配置节路径
///
public string SectionPath { get; }
///
/// 是否生成扩展方法
///
public bool GenerateExtension { get; set; } = true;
///
/// 创建配置节特性
///
public CfgSectionAttribute(string sectionPath = """")
{
SectionPath = sectionPath;
}
}
}
";
}
///
/// 配置类信息
///
internal sealed record ConfigClassInfo(
string Namespace,
string ClassName,
string FullTypeName,
string SectionPath,
bool GenerateExtension,
ImmutableArray Properties);
///
/// 属性信息
///
internal sealed record PropertyInfo(
string Name,
string TypeName,
TypeKind TypeKind,
string? ElementTypeName,
string? KeyTypeName,
bool IsNullable);
///
/// 类型种类
///
internal enum TypeKind
{
Unknown,
Simple,
Array,
List,
HashSet,
Dictionary,
Complex
}