如何为C# Source Generator生成的警告添加自动代码修复建议?
实现C# Source Generator诊断与自动代码修复的关联方案
核心思路
Source Generator输出的诊断和Analyzer的诊断共用一套体系,代码修复的实现逻辑也完全一致——通过诊断ID绑定来关联诊断与对应的修复动作,以下是具体实现步骤:
1. 定义固定的诊断规则
首先在Source Generator中,你需要创建一个明确的DiagnosticDescriptor实例,确保诊断有唯一ID、描述等信息,这是后续关联修复的核心标识:
private static readonly DiagnosticDescriptor MyFixableWarning = new( id: "SG0001", // 唯一诊断ID,修复提供者将通过此ID关联 title: "需要自动修复的代码问题", messageFormat: "代码片段 '{0}' 不符合规范,可自动修复", category: "CustomSourceGenerator", defaultSeverity: DiagnosticSeverity.Warning, // 支持Hidden级别,只需改为DiagnosticSeverity.Hidden isEnabledByDefault: true, description: "Source Generator检测到的可自动修复代码问题");
在Generator的Execute方法中,当检测到需要警告的场景时,通过GeneratorExecutionContext输出诊断,并关联具体的代码位置:
public void Execute(GeneratorExecutionContext context) { // 模拟检测到问题的逻辑 foreach (var syntaxTree in context.Compilation.SyntaxTrees) { var root = syntaxTree.GetRoot(context.CancellationToken); var targetNodes = root.DescendantNodes().OfType<IdentifierNameSyntax>(); foreach (var node in targetNodes) { if (node.Identifier.Text == "NeedFix") { // 输出诊断,绑定到具体代码节点 var diagnostic = Diagnostic.Create( MyFixableWarning, node.GetLocation(), node.Identifier.Text); context.ReportDiagnostic(diagnostic); } } } }
2. 实现代码修复提供者(Code Fix Provider)
创建一个继承自CodeFixProvider的类,通过指定FixableDiagnosticIds来关联上面定义的诊断ID,然后编写具体的修复逻辑:
基础框架实现
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MyCodeFixProvider))] [Shared] public class MyCodeFixProvider : CodeFixProvider { // 绑定要处理的诊断ID,必须和Source Generator输出的诊断ID完全一致 public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create("SG0001"); // 指定批量修复的提供者,用默认实现即可 public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; // 注册修复动作 public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic = context.Diagnostics.First(); var diagnosticSpan = diagnostic.Location.SourceSpan; // 定位到触发诊断的语法节点 var targetNode = root.FindToken(diagnosticSpan.Start).Parent; if (targetNode == null) return; // 注册修复动作,指定显示名称、修复逻辑、等价键(用于批量修复) context.RegisterCodeFix( CodeAction.Create( title: "自动修复代码问题", createChangedDocument: c => FixCodeIssueAsync(context.Document, targetNode, c), equivalenceKey: "SG0001_Fix"), diagnostic); } }
编写具体修复逻辑
实现FixCodeIssueAsync方法,根据你的需求修改代码,示例为替换标识符:
private async Task<Document> FixCodeIssueAsync(Document document, SyntaxNode targetNode, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // 创建新的语法节点,替换原有节点(示例:将NeedFix改为FixedCode) var newNode = SyntaxFactory.IdentifierName("FixedCode") .WithTriviaFrom(targetNode); // 保留原有注释、空格等 // 生成新的语法树并返回修改后的文档 var newRoot = root.ReplaceNode(targetNode, newNode); return document.WithSyntaxRoot(newRoot); }
3. 项目配置与关联注意事项
- 项目设置:确保包含Source Generator和Code Fix Provider的项目,在
.csproj中添加以下配置,标记为Roslyn组件:<PropertyGroup> <LangVersion>latest</LangVersion> <IsRoslynComponent>true</IsRoslynComponent> <Nullable>enable</Nullable> </PropertyGroup> - ID一致性:必须保证Source Generator中定义的诊断ID和Code Fix Provider的
FixableDiagnosticIds完全匹配,否则无法关联。 - Hidden级别诊断:如果需要用Hidden级别,只需将
DiagnosticDescriptor的defaultSeverity改为DiagnosticSeverity.Hidden,Visual Studio依然会为其显示修复选项(可通过快速操作菜单触发)。
4. 验证与测试
创建测试项目引用该组件项目,编写包含NeedFix标识符的代码,Visual Studio会自动显示警告,点击灯泡图标即可看到“自动修复代码问题”的选项,执行后代码会被替换为FixedCode。
内容的提问来源于stack exchange,提问作者user3797758
相关产品推荐
相关产品推荐

