mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
refactor(source-generators): 提取通用诊断描述符并更新代码生成器
- 将 ClassMustBePartial 诊断描述符移至 CommonDiagnostics 类 - 更新 GodotLoggerGenerator 和 LoggerGenerator 引用通用诊断 - 添加 ContextAwareGenerator 实现上下文感知功能 - 创建 ContextAwareAttribute 标记需要自动实现 IContextAware 的类 - 在项目中添加对 GFramework.SourceGenerators.Common 的引用 - 更新诊断规则 ID 命名规范 - 修复 AnalyzerReleases 文件格式 - 添加 nullable enable 配置 - 在解决方案文件中添加新项目引用
This commit is contained in:
parent
017870421e
commit
a888e76842
@ -3,7 +3,6 @@
|
||||
|
||||
### New Rules
|
||||
|
||||
Rule ID | Category | Severity | Notes
|
||||
------------|--------------------------|----------|------------------------
|
||||
GFLOG001 | GFramework.Godot.Logging | Error | GodotLoggerDiagnostics
|
||||
GFW_LOG001 | GFramework.Godot.Logging | Warning | GodotLoggerDiagnostics
|
||||
Rule ID | Category | Severity | Notes
|
||||
----------------------|--------------------------|----------|------------------------
|
||||
GF_Godot_Logging_001 | GFramework.Godot.Logging | Warning | GodotLoggerDiagnostics
|
||||
@ -23,15 +23,11 @@
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Shipped.md"/>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Generator 需要引用 Attributes 项目,但不作为运行时依赖 -->
|
||||
<ProjectReference Include="..\GFramework.Godot.SourceGenerators.Attributes\GFramework.Godot.SourceGenerators.Attributes.csproj"
|
||||
PrivateAssets="all"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"/>
|
||||
</ItemGroup>
|
||||
<!-- 将 Generator 和 Attributes DLL 打包为 Analyzer -->
|
||||
<ItemGroup>
|
||||
@ -41,4 +37,8 @@
|
||||
<None Include="$(OutputPath)\GFramework.Godot.SourceGenerators.Attributes.dll" Pack="true"
|
||||
PackagePath="analyzers/dotnet/cs" Visible="false"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Shipped.md"/>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -7,25 +7,6 @@ namespace GFramework.Godot.SourceGenerators.logging;
|
||||
/// </summary>
|
||||
internal static class GodotLoggerDiagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// 诊断描述符:标识使用[GodotLog]特性的类必须声明为partial
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ID: GFLOG001
|
||||
/// 严重性: Error
|
||||
/// 分类: GFramework.Godot.Logging
|
||||
/// </remarks>
|
||||
public static readonly DiagnosticDescriptor MustBePartial =
|
||||
new(
|
||||
"GFLOG001",
|
||||
"Class must be partial",
|
||||
"Class '{0}' must be declared as partial to use [GodotLog]",
|
||||
"GFramework.Godot.Logging",
|
||||
DiagnosticSeverity.Error,
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 诊断描述符:标识GodotLogAttribute无法在指定类上生成Logger
|
||||
/// </summary>
|
||||
@ -35,7 +16,7 @@ internal static class GodotLoggerDiagnostics
|
||||
/// 分类: GFramework.Godot.Logging
|
||||
/// </remarks>
|
||||
public static readonly DiagnosticDescriptor LogAttributeInvalid = new(
|
||||
"GFW_LOG001",
|
||||
"GF_Godot_Logging_001",
|
||||
"GodotLogAttribute cannot generate Logger",
|
||||
"GodotLogAttribute on class '{0}' is ineffective: {1}",
|
||||
"GFramework.Godot.Logging",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GFramework.SourceGenerators.Common.diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
@ -63,7 +64,7 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator
|
||||
if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||||
{
|
||||
spc.ReportDiagnostic(Diagnostic.Create(
|
||||
GodotLoggerDiagnostics.MustBePartial,
|
||||
CommonDiagnostics.ClassMustBePartial,
|
||||
classDecl.Identifier.GetLocation(),
|
||||
classSymbol.Name));
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Attributes.rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记该类需要自动实现 IContextAware
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
; Shipped analyzer releases
|
||||
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
; Unshipped analyzer release
|
||||
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
|
||||
|
||||
### New Rules
|
||||
|
||||
Rule ID | Category | Severity | Notes
|
||||
---------|-------------------|----------|-------------------
|
||||
GFC001 | GFramework.Common | Error | CommonDiagnostics
|
||||
@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.14.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Shipped.md"/>
|
||||
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,29 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace GFramework.SourceGenerators.Common.diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供通用诊断描述符的静态类
|
||||
/// </summary>
|
||||
public static class CommonDiagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义类必须为partial的诊断描述符
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 诊断ID: GF001
|
||||
/// 诊断消息: "Class '{0}' must be declared partial for code generation"
|
||||
/// 分类: GFramework.Common
|
||||
/// 严重性: Error
|
||||
/// 是否启用: true
|
||||
/// </remarks>
|
||||
public static readonly DiagnosticDescriptor ClassMustBePartial =
|
||||
new(
|
||||
"GFC001",
|
||||
"Class must be partial",
|
||||
"Class '{0}' must be declared partial for code generation",
|
||||
"GFramework.Common",
|
||||
DiagnosticSeverity.Error,
|
||||
true
|
||||
);
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
### New Rules
|
||||
|
||||
| Rule ID | Category | Severity | Notes |
|
||||
|------------|--------------------|----------|-------------|
|
||||
| GFLOG001 | GFramework.Logging | Error | Diagnostics |
|
||||
| GFW_LOG001 | GFramework.Logging | Warning | Diagnostics |
|
||||
Rule ID | Category | Severity | Notes
|
||||
----------------|--------------------------|----------|-------------------
|
||||
GF_Logging_001 | GFramework.Godot.Logging | Warning | LoggerDiagnostics
|
||||
@ -16,6 +16,7 @@
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all"/>
|
||||
@ -27,6 +28,7 @@
|
||||
<!-- Generator 需要引用 Attributes 项目,但不作为运行时依赖 -->
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Attributes\GFramework.SourceGenerators.Attributes.csproj"
|
||||
PrivateAssets="all"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 将 Generator 和 Attributes DLL 打包为 Analyzer -->
|
||||
|
||||
@ -7,28 +7,12 @@ namespace GFramework.SourceGenerators.logging;
|
||||
/// </summary>
|
||||
internal static class LoggerDiagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义诊断描述符:要求使用[Log]特性的类必须声明为partial
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 当类使用[Log]特性但未声明为partial时,编译器将报告此错误
|
||||
/// </remarks>
|
||||
public static readonly DiagnosticDescriptor MustBePartial =
|
||||
new(
|
||||
"GFLOG001",
|
||||
"Class must be partial",
|
||||
"Class '{0}' must be declared partial to use [Log]",
|
||||
"GFramework.Logging",
|
||||
DiagnosticSeverity.Error,
|
||||
true
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// 定义诊断描述符:LogAttribute无法生成Logger的错误情况
|
||||
/// </summary>
|
||||
public static readonly DiagnosticDescriptor LogAttributeInvalid =
|
||||
new(
|
||||
"GFW_LOG001",
|
||||
"GF_Logging_001",
|
||||
"LogAttribute cannot generate Logger",
|
||||
"LogAttribute on class '{0}' is ineffective: {1}",
|
||||
"GFramework.Godot.Logging",
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GFramework.SourceGenerators.Common.diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
@ -64,7 +65,7 @@ public sealed class LoggerGenerator : IIncrementalGenerator
|
||||
if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||||
{
|
||||
spc.ReportDiagnostic(Diagnostic.Create(
|
||||
LoggerDiagnostics.MustBePartial,
|
||||
CommonDiagnostics.ClassMustBePartial,
|
||||
classDecl.Identifier.GetLocation(),
|
||||
classSymbol.Name));
|
||||
|
||||
|
||||
114
GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
Normal file
114
GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GFramework.SourceGenerators.Common.diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace GFramework.SourceGenerators.rule;
|
||||
|
||||
[Generator]
|
||||
public sealed class ContextAwareGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeMetadataName =
|
||||
"GFramework.SourceGenerators.Attributes.rule.ContextAwareAttribute";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 1. 找到所有 class 声明
|
||||
var classDeclarations = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => node is ClassDeclarationSyntax,
|
||||
transform: static (ctx, _) => GetCandidate(ctx)
|
||||
)
|
||||
.Where(static s => s is not null);
|
||||
|
||||
// 2. 生成代码
|
||||
context.RegisterSourceOutput(
|
||||
classDeclarations,
|
||||
static (spc, source) => Generate(spc, source!)
|
||||
);
|
||||
}
|
||||
|
||||
private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
|
||||
if (classDecl.AttributeLists.Count == 0)
|
||||
return null;
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDecl)
|
||||
is not { } symbol)
|
||||
return null;
|
||||
|
||||
return Enumerable.Any(symbol.GetAttributes(),
|
||||
attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName)
|
||||
? symbol
|
||||
: null;
|
||||
}
|
||||
|
||||
|
||||
private static void Generate(
|
||||
SourceProductionContext context,
|
||||
INamedTypeSymbol symbol)
|
||||
{
|
||||
var syntax = symbol.DeclaringSyntaxReferences
|
||||
.FirstOrDefault()?
|
||||
.GetSyntax() as ClassDeclarationSyntax;
|
||||
|
||||
if (syntax is null || !syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(
|
||||
CommonDiagnostics.ClassMustBePartial,
|
||||
syntax?.Identifier.GetLocation(),
|
||||
symbol.Name
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||||
? null
|
||||
: symbol.ContainingNamespace.ToDisplayString();
|
||||
|
||||
var source = GenerateSource(ns, symbol);
|
||||
|
||||
context.AddSource(
|
||||
$"{symbol.Name}.ContextAware.g.cs",
|
||||
source
|
||||
);
|
||||
}
|
||||
|
||||
private static string GenerateSource(string? ns, INamedTypeSymbol symbol)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("// <auto-generated/>");
|
||||
sb.AppendLine("#nullable enable");
|
||||
|
||||
if (ns is not null)
|
||||
{
|
||||
sb.AppendLine($"namespace {ns};");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
sb.AppendLine($"partial class {symbol.Name} : GFramework.Core.rule.IContextAware");
|
||||
sb.AppendLine("{");
|
||||
|
||||
sb.AppendLine(
|
||||
" protected GFramework.Core.architecture.IArchitectureContext Context { get; private set; } = null!;");
|
||||
|
||||
sb.AppendLine("""
|
||||
void GFramework.Core.rule.IContextAware.SetContext(
|
||||
GFramework.Core.architecture.IArchitectureContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
""");
|
||||
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,7 @@
|
||||
<None Remove="GFramework.SorceGenerators\**"/>
|
||||
<None Remove="GFramework.SourceGenerators\**"/>
|
||||
<None Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<None Remove="GFramework.SourceGenerators.Common\**"/>
|
||||
</ItemGroup>
|
||||
<!-- 聚合核心模块 -->
|
||||
<ItemGroup>
|
||||
@ -68,6 +69,7 @@
|
||||
<Compile Remove="GFramework.SorceGenerators\**"/>
|
||||
<Compile Remove="GFramework.SourceGenerators\**"/>
|
||||
<Compile Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<Compile Remove="GFramework.SourceGenerators.Common\**"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="GFramework.Core\**"/>
|
||||
@ -78,5 +80,13 @@
|
||||
<EmbeddedResource Remove="GFramework.SorceGenerators\**"/>
|
||||
<EmbeddedResource Remove="GFramework.SourceGenerators\**"/>
|
||||
<EmbeddedResource Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<EmbeddedResource Remove="GFramework.SourceGenerators.Common\**"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.14.0"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Remove="AnalyzerReleases.Shipped.md"/>
|
||||
<AdditionalFiles Remove="AnalyzerReleases.Unshipped.md"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGene
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGenerators.Attributes", "GFramework.Godot.SourceGenerators.Attributes\GFramework.Godot.SourceGenerators.Attributes.csproj", "{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators.Common", "GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj", "{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -54,5 +56,9 @@ Global
|
||||
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADiagnostic_002Ecs_002Fl_003AD_0021_003FTool_003FDevelopment_0020Tools_003FJetBrains_003F_002EJetBrains_003F_002ERider_003Fconfig_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8fed4175e0a54d839582ab555761a2de4d5128_003Ff3_003Fd48d28bd_003FDiagnostic_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInputEventAction_002Ecs_002Fl_003AD_0021_003FTool_003FDevelopment_0020Tools_003FJetBrains_003F_002EJetBrains_003F_002ERider_003Fconfig_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1c378f459c054fecaf4484a0fa6d44c055a800_003F18_003F33b52a1c_003FInputEventAction_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
Loading…
x
Reference in New Issue
Block a user