解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
4.35 KiB
X
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.Text;

namespace NewLife.SourceGenerator;

/// <summary>Json 成员访问生成器。扫描 <c>[JsonSerializable(typeof(T))]</c> 标记,为 T 的公开实例属性与字段生成静态读写委托注册,JsonHelper 序列化时优先使用,避免反射与表达式编译</summary>
/// <remarks>
/// 依赖 System.Text.Json 的 <c>JsonSerializableAttribute</c>(net5+ 共享框架自带,无需额外包)。
/// 只生成可直接静态访问的成员(public、非只读/init-only 赋值、非元组/指针/ref struct、类型可访问);
/// 未标记类型与不可生成成员保持原反射路径,行为不变。
/// </remarks>
[Generator(LanguageNames.CSharp)]
public sealed class JsonSerializableGenerator : IIncrementalGenerator
{
    #region 生成入口
    /// <summary>初始化增量生成管线</summary>
    /// <param name="context">生成上下文</param>
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var lines = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "System.Text.Json.Serialization.JsonSerializableAttribute",
                predicate: static (node, token) => true,
                transform: static (ctx, token) => Extract(ctx, token))
            .SelectMany(static (items, token) => items)
            .Where(static x => x != null);

        var combined = context.CompilationProvider.Combine(lines.Collect());

        context.RegisterSourceOutput(combined, static (spc, pair) => Emit(spc, pair.Left, pair.Right));
    }
    #endregion

    #region 提取
    // 提取标记类型的所有可生成成员注册行(值语义字符串,增量缓存友好)
    private static ImmutableArray<String?> Extract(GeneratorAttributeSyntaxContext context, CancellationToken token)
    {
        var builder = ImmutableArray.CreateBuilder<String?>();
        var compilation = context.SemanticModel.Compilation;

        foreach (var attribute in context.Attributes)
        {
            if (attribute.ConstructorArguments.Length == 0) continue;
            if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) continue;

            MemberLineBuilder.AddMembers(builder, type, compilation);
        }

        return builder.ToImmutable();
    }

    #endregion

    #region 产出
    // 输出生成源码。仅 .NET 5+(含模块初始化器)且在可用语言版本下生成
    private static void Emit(SourceProductionContext context, Compilation compilation, ImmutableArray<String?> items)
    {
        if (items.IsDefaultOrEmpty) return;

        var set = new SortedSet<String>(StringComparer.Ordinal);
        foreach (var item in items)
        {
            if (!String.IsNullOrEmpty(item)) set.Add(item!);
        }
        if (set.Count == 0) return;

        var tree = compilation.SyntaxTrees.FirstOrDefault();
        if (tree?.Options is not CSharpParseOptions options) return;
        if (!options.PreprocessorSymbolNames.Contains("NET5_0_OR_GREATER")) return;

        var builder = new StringBuilder();
        builder.AppendLine("// <auto-generated/>");
        if ((Int32)options.LanguageVersion >= (Int32)LanguageVersion.CSharp8) builder.AppendLine("#nullable enable");
        builder.AppendLine();
        builder.AppendLine("namespace NewLife.SourceGenerator.Generated");
        builder.AppendLine("{");
        builder.AppendLine("    /// <summary>源生成的 Json 成员访问注册。NewLife.Core 源生成器自动生成,请勿修改</summary>");
        builder.AppendLine("    internal static class JsonMemberRegistrar");
        builder.AppendLine("    {");
        builder.AppendLine("        [global::System.Runtime.CompilerServices.ModuleInitializer]");
        builder.AppendLine("        internal static void Initialize()");
        builder.AppendLine("        {");
        foreach (var line in set)
        {
            builder.Append("            ").AppendLine(line);
        }
        builder.AppendLine("        }");
        builder.AppendLine("    }");
        builder.AppendLine("}");

        context.AddSource("JsonMemberRegistrar.g.cs", SourceText.From(builder.ToString(), Encoding.UTF8));
    }
    #endregion
}