使用 `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
使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾的更多相关文章
- 建立标准编码规则(三)-CodeFixProvider 给代码分析器增加修复建议
给代码分析器增加修复建议 既然代码分析器,向代码编写者提出了错误或警告,那么有没有可能向代码编写者提交有效的改进建议? 相对于 DiagnosticAnalyzer,代码修复继承与 CodeFixPr ...
- .Net 异步方法, await async 使用
最近朋友问起await 和 async第一次听说这个await ,就查了一下这个await使用在于 异步方法async 中,中文意思就是等待,经过一系列的百度参考简单的明白了这个东西的意思, 异步 ...
- SpringMvc Controller请求链接忽略大小写(包含拦截器)及@ResponseBody返回String中文乱码处理
SpringMvc Controller请求链接忽略大小写(包含拦截器)及@ResponseBody返回String中文乱码处理... @RequestMapping(value = "/t ...
- vue props 下有验证器 validator 验证数据返回true false后,false给default值
vue props 下有验证器 validator 验证数据返回true false后,false给default值 props: { type: { validator (value) { retu ...
- Extjs的数据读取器store和后台返回类型简单解析
工作中用到了Extjs,从后台获取数据的时候,用到了extjs自己的Ext.data.store方法,然后封装了ExtGridReturn方法, 目的:前台用到Ext.data.store读取从后台传 ...
- struts2拦截器实现session超时返回登录页面(iframe下跳转到其父页面)
需求:session超时时,返回登录页面,由于页面嵌套在iframe下,因此要跳转到登录页面的父页面,但是首页,登录页面等不需要进行跳转 实现: java文件:SessionIterceptor.ja ...
- 【踩坑】.NET异步方法不标记async,Task<int> 返回值 return default问题
在.NET中,返回类型为 Task<T> 的方法并不一定要标记为 async.这是因为 async 关键字只是用来告诉编译器该方法中包含异步操作,并且可以使用 await 和其他异步特 ...
- WebApi 通过拦截器设置特定的返回格式
public class ActionFilter : ActionFilterAttribute { /// <summary> /// Action执行之后由MVC框架调用 /// & ...
- C# 4 中使用迭代器的等待任务
介绍 可能你已经阅读 C#5 关于 async 和 await 关键字以及它们如何帮助简化异步编程的,可惜的是在升级VS2010后短短两年时间,任然没有准备好升级到VS2012,在VS2010和C#4 ...
- .NET 7 RC 2 发布,倒计时一个月发布正式版
微软2022-10-22 发布了 .NET 7 RC 2,下一站是.NET 7正式发布,就在下个月Net Conf 2022(11月8日)期间正式发布. 经过长达一年时间的开发,.NET 7 规划的所 ...
随机推荐
- [oeasy]python0144_try的完全体_否则_else_最终_finally
try的完全体 回忆上次内容 上次细化了主控程序(main.py) 导入(get_fruits.py) 处理(process.py) 输出(output.py) 使用了 try 结构 try ...
- oeasy教您玩转linux010210管理应用aptitude
上一部分我们都讲了什么? 下载并运行了 hollywood hollywood 更新了源的信息 sudo apt update 查看所有已经安装的软件包 # dpkg deiban 本地包管理 dpk ...
- [MRCTF2020]Ezpop(反序列化)
打开题目即可得源码 Welcome to index.php <?php //flag is in flag.php //WTF IS THIS? //Learn From https://ct ...
- MySQL 纵表转横表查询实现
纵表转横表查询实现 By:授客 QQ:1033553122 欢迎加入全国软件测试交流群:7156436 实践环境 MySQL 5.7 创建测试表 CREATE TABLE tb_test ( id I ...
- 一文详解 JuiceFS 读性能:预读、预取、缓存、FUSE 和对象存储
在高性能计算场景中,往往采用全闪存架构和内核态并行文件系统,以满足性能要求.随着数据规模的增加和分布式系统集群规模的增加,全闪存的高成本和内核客户端的运维复杂性成为主要挑战. JuiceFS,是一款全 ...
- 一款基于Fluent设计风格、现代化的WPF UI控件库
前言 今天大姚给大家分享一款基于Fluent设计风格.开源(MIT License).现代化的WPF UI控件库,它提供直观的设计.主题.导航和全新的沉浸式控件,全部都是原生且无缝地集成在一起:WPF ...
- Postman汉化成中文版
postman安装默认是英文版,为使用方便使用汉化包转成中文版 1.查看本地安装的postman版本:Settings->About 2.根据postman的版本下载对应的汉化包,汉化包网址 3 ...
- 硬件开发笔记(二十九):TPS54331电源设计(二):12V转3.3V和12V转4V原理图设计
前言 电源供电电路设计很重要,为了更好的给对硬件设计有需求的人,特意将电源设计的基础过程描述出来. 紧接前一篇12V转5V的,本篇设计常用的12V转3.3V电路,不常用的12V转4V电路. ...
- 【MySQL】拆分经纬度字段
数据结构: 表中一字段存储经度和纬度: +---------------------------+ | INSTALL_LOLA_NUM | +---------------------------+ ...
- 【Windows】开放共享目录
在项目里面做数据迁移时发现,WindowsServer的多个主机可以进行磁盘共享访问 但是自己设置是灰白无法点击的 文件目录共享还是可以进行设置的 1.找到自己需要共享的目录,右键选择[属性],并找到 ...