首页 > 极客资料 博客日记
使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾
2024-09-13 17:30:03极客资料围观23次
文章使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾分享给大家,欢迎收藏极客之家,专注分享技术知识
之前写过一篇使用修复器帮助添加头部注释文本的功能,今天使用Roslyn的代码修复器对异步返回方法规范化的功能
实现分析器
首先需要实现分析器,使用RegisterSyntaxNodeAction
,分析所有SyntaxKind.MethodDeclaration
的语法类型,
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncMethodNameAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "GEN051";
private static readonly LocalizableString Title = "将异步方法名改为以Async结尾";
private static readonly LocalizableString MessageFormat = "将异步方法名改为以Async结尾";
private static readonly LocalizableString Description = "将异步方法名改为以Async结尾.";
private const string Category = "Documentation";
private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];
public override void Initialize(AnalysisContext context)
{
if (context is null)
return;
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
}
// 异步方法名称应该以Async结尾
private const string AsyncSuffix = "Async";
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
//如果方法包含async修饰的情况
if (methodDeclaration.Modifiers.Any(SyntaxKind.AsyncKeyword))
{
if (!methodDeclaration.Identifier.Text.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase))
{
var diagnostic = Diagnostic.Create(Rule, methodDeclaration.Identifier.GetLocation(), methodDeclaration.Identifier.Text);
context.ReportDiagnostic(diagnostic);
}
}
var returnType = methodDeclaration.ReturnType;
//如果返回类型为Task或者Task<T>,或者ValueTask<T>,ValueTask 则方法名应该以Async结尾
// 判断返回类型是否为 Task 或 ValueTask
if (returnType is IdentifierNameSyntax identifierName)
{
if (identifierName.Identifier.Text == "Task" || identifierName.Identifier.Text == "ValueTask")
{
if (!methodDeclaration.Identifier.Text.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase))
{
var diagnostic = Diagnostic.Create(Rule, methodDeclaration.Identifier.GetLocation(), methodDeclaration.Identifier.Text);
context.ReportDiagnostic(diagnostic);
}
}
}
else if (returnType is GenericNameSyntax genericName && (genericName.Identifier.Text == "Task" || genericName.Identifier.Text == "ValueTask"))
{
if (!methodDeclaration.Identifier.Text.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase))
{
var diagnostic = Diagnostic.Create(Rule, methodDeclaration.Identifier.GetLocation(), methodDeclaration.Identifier.Text);
context.ReportDiagnostic(diagnostic);
}
}
}
}
在 Initialize
方法中,注册了一个语法节点操作来处理 MethodDeclarationSyntax
节点,主要是考虑方法是否async
关键字标注,或者返回的类型是否是Task
或者ValueTask
,如果这些条件满足则判断方法名称MethodDeclaration.Identifier
是否为Async
结尾.如果存在这样的问题 那么创建一个诊断并报告。
实现修复器
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AsyncMethodNameCodeFixProvider))]
[Shared]
internal class AsyncMethodNameCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => [AsyncMethodNameAnalyzer.DiagnosticId];
public sealed override FixAllProvider GetFixAllProvider() =>
WellKnownFixAllProviders.BatchFixer;
private const string Title = "将异步方法名改为以Async结尾";
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics[0];
var diagnosticSpan = diagnostic.Location.SourceSpan;
context.RegisterCodeFix(
CodeAction.Create(
title: Title,
createChangedDocument:
c =>
FixDocumentAsync(context.Document, diagnostic, c),
equivalenceKey: Title),
diagnostic);
return Task.CompletedTask;
}
private const string AsyncSuffix = "Async";
private static async Task<Document> FixDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken c)
{
var root = await document.GetSyntaxRootAsync(c).ConfigureAwait(false);
if (root == null)
return document;
var node = root.FindNode(diagnostic.Location.SourceSpan);
var methodDeclaration = (MethodDeclarationSyntax)node;
var newName = $"{methodDeclaration.Identifier.Text}{AsyncSuffix}";
var newRoot = root.ReplaceNode(methodDeclaration, methodDeclaration.WithIdentifier(SyntaxFactory.Identifier(newName)));
return document.WithSyntaxRoot(newRoot);
}
}
修复器的实现就更加简单了,我们通过诊断信息和Document
就可以定位到有问题的方法本身,然后使用WithIdentifier
和SyntaxFactory.Identifier
将方法名称修正为正确的值 并返回修复的Document即可!
预览效果
最后我们看一下效果
编译时返回的警告信息
编辑文档是产生的提示信息
点击提示即可修复代码问题
最后
其实这个属于代码习惯或者代码风格的问题,个人是比较推荐异步方法还是加上后缀的,毕竟有了这个规范我们一看方法就能知道这是一个异步方法 😃
最后你可以使用我发布的nuget包体验:
dotnet add package Biwen.AutoClassGen
源代码我发布到了GitHub,欢迎star! https://github.com/vipwan/Biwen.AutoClassGen
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
上一篇:架构师备考的一些思考(四)
下一篇:吊打面试官!从多维度理解架构
相关文章
最新发布
- Nuxt.js 应用中的 prerender:routes 事件钩子详解
- 【问题解决】Tomcat由低于8版本升级到高版本使用Tomcat自带连接池报错无法找到表空间的问题
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Vision Kit
- 六、Spring Boot集成Spring Security之前后分离认证流程最佳方案
- 《JVM第7课》堆区
- .NET 8 高性能跨平台图像处理库 ImageSharp
- 还在为慢速数据传输苦恼?Linux 零拷贝技术来帮你!
- 刚毕业,去做边缘业务,还有救吗?
- 如何避免 HttpClient 丢失请求头:通过 HttpRequestMessage 解决并优化
- 让性能提升56%的Vue3.5响应式重构之“版本计数”