Roslyn代码修复:创建带单行注释的格式化空方法
Roslyn代码修复:生成格式规范的仅含单行注释的空方法
修改前后对比
修改前:
public class Bla { public void Blub() { Console.WriteLine(""); } }
修改后期望效果:
public class Bla { public void Blub() { // I told you so } }
三次尝试及问题
版本1 - 通过WithStatements添加注释
代码:
private static async Task<Document> RemoveContainOfDownMethod(Document document, BaseMethodDeclarationSyntax downMethodDeclaration, CancellationToken cancellationToken) { SyntaxTree? documentSyntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); if (documentSyntaxTree == null || downMethodDeclaration.Body == null) { return document; } var commentTrivia = SyntaxFactory.Comment("// I told you so."); var emptyBlock = SyntaxFactory.Block() .WithOpenBraceToken(downMethodDeclaration.Body.OpenBraceToken) .WithCloseBraceToken(downMethodDeclaration.Body.CloseBraceToken) .WithStatements(SyntaxFactory.SingletonList<StatementSyntax>( SyntaxFactory.EmptyStatement().WithLeadingTrivia(commentTrivia))); CompilationUnitSyntax newDocument = documentSyntaxTree.GetCompilationUnitRoot(cancellationToken) .ReplaceNode( downMethodDeclaration.Body, emptyBlock .WithLeadingTrivia(downMethodDeclaration.Body.GetLeadingTrivia()) .WithTrailingTrivia(downMethodDeclaration.Body.GetTrailingTrivia())); return document.WithSyntaxRoot(newDocument); }
生成结果:
public class Bla { public void Blub() { // I told you so ; } }
问题:
- 存在尾随分号
版本2 - 通过WithCloseBraceToken添加注释
代码:
private static async Task<Document> RemoveContainOfDownMethod(Document document, BaseMethodDeclarationSyntax downMethodDeclaration, CancellationToken cancellationToken) { SyntaxTree? documentSyntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); if (documentSyntaxTree == null || downMethodDeclaration.Body == null) { return document; } SyntaxTrivia commentTrivia = SyntaxFactory.Comment("// I told you so"); BlockSyntax emptyBlock = SyntaxFactory.Block() .WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken)) .WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(commentTrivia)); CompilationUnitSyntax newDocument = documentSyntaxTree.GetCompilationUnitRoot(cancellationToken) .ReplaceNode( downMethodDeclaration.Body, emptyBlock .WithLeadingTrivia(downMethodDeclaration.Body.GetLeadingTrivia()) .WithTrailingTrivia(downMethodDeclaration.Body.GetTrailingTrivia())); return document.WithSyntaxRoot(newDocument); }
生成结果:
public class Bla { public void Blub() { // I told you so } }
问题:
- 方法闭合大括号前缺少换行
版本3 - 通过WithCloseBraceToken添加注释并使用NormalizeWhitespace
代码:
private static async Task<Document> RemoveContainOfDownMethod(Document document, BaseMethodDeclarationSyntax downMethodDeclaration, CancellationToken cancellationToken) { SyntaxTree? documentSyntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); if (documentSyntaxTree == null || downMethodDeclaration.Body == null) { return document; } SyntaxTrivia commentTrivia = SyntaxFactory.Comment("// I told you so"); BlockSyntax emptyBlock = SyntaxFactory.Block() .WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken)) .WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(commentTrivia)); CompilationUnitSyntax newDocument = documentSyntaxTree.GetCompilationUnitRoot(cancellationToken) .ReplaceNode( downMethodDeclaration.Body, emptyBlock .WithLeadingTrivia(downMethodDeclaration.Body.GetLeadingTrivia()) .WithTrailingTrivia(downMethodDeclaration.Body.GetTrailingTrivia())) .NormalizeWhitespace(); return document.WithSyntaxRoot(newDocument); }
生成结果:
public class Bla { public void Blub() { // I told you so } }
问题:
- 注释缩进错误
正确实现方案
核心思路是给空Block的闭合大括号添加包含缩进、换行、注释的完整Trivia集合,同时复用原方法块的格式信息,确保输出和原代码风格一致。
private static async Task<Document> RemoveContainOfDownMethod(Document document, BaseMethodDeclarationSyntax downMethodDeclaration, CancellationToken cancellationToken) { SyntaxTree? documentSyntaxTree = await document.GetSyntaxTreeAsync(cancellationToken); if (documentSyntaxTree == null || downMethodDeclaration.Body == null) { return document; } // 提取原方法内的缩进格式,保证注释缩进和原代码一致 var originalIndent = downMethodDeclaration.Body.Statements.FirstOrDefault()?.GetLeadingTrivia() .Where(t => t.IsKind(SyntaxKind.WhitespaceTrivia) || t.IsKind(SyntaxKind.EndOfLineTrivia)) .TakeWhile(t => !t.IsKind(SyntaxKind.EndOfLineTrivia)) .Aggregate("", (acc, t) => acc + t.ToFullString()); // 构建闭合大括号的前导Trivia:缩进 + 换行 + 注释 + 换行 var commentTriviaList = SyntaxFactory.TriviaList( SyntaxFactory.Whitespace(originalIndent ?? " "), SyntaxFactory.EndOfLine("\r\n"), SyntaxFactory.Comment("// I told you so"), SyntaxFactory.EndOfLine("\r\n") ); // 创建新的空Block,复用原开、闭大括号Token并设置正确Trivia var emptyBlock = SyntaxFactory.Block() .WithOpenBraceToken(downMethodDeclaration.Body.OpenBraceToken) .WithCloseBraceToken(downMethodDeclaration.Body.CloseBraceToken.WithLeadingTrivia(commentTriviaList)) .WithLeadingTrivia(downMethodDeclaration.Body.GetLeadingTrivia()) .WithTrailingTrivia(downMethodDeclaration.Body.GetTrailingTrivia()); var newRoot = documentSyntaxTree.GetCompilationUnitRoot(cancellationToken) .ReplaceNode(downMethodDeclaration.Body, emptyBlock); return document.WithSyntaxRoot(newRoot); }
关键说明
- 动态提取缩进:从原方法内的语句获取缩进格式,避免硬编码缩进导致的风格不匹配
- 完整Trivia集合:给闭合大括号的前导Trivia添加缩进、换行、注释、换行,确保注释单独占一行且位置正确
- 复用原Token:保留原方法块的开、闭大括号Token,避免重新生成Token丢失原格式信息
内容的提问来源于stack exchange,提问作者mu88
相关产品推荐
相关产品推荐

