首页 > 其他分享 >使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾

使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾

时间:2024-09-13 17:25:07浏览次数:9  
标签:diagnostic context Roslyn var Async Identifier methodDeclaration 修复器

之前写过一篇使用修复器帮助添加头部注释文本的功能,今天使用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就可以定位到有问题的方法本身,然后使用WithIdentifierSyntaxFactory.Identifier将方法名称修正为正确的值 并返回修复的Document即可!

预览效果

最后我们看一下效果

编译时返回的警告信息
image
编辑文档是产生的提示信息
image
点击提示即可修复代码问题
image

最后

其实这个属于代码习惯或者代码风格的问题,个人是比较推荐异步方法还是加上后缀的,毕竟有了这个规范我们一看方法就能知道这是一个异步方法

标签:diagnostic,context,Roslyn,var,Async,Identifier,methodDeclaration,修复器
From: https://www.cnblogs.com/vipwan/p/18412585

相关文章

  • 开发故事:一个 @Async 如何搞瘫整个微服务系统
    嘿,大家好!我是小米,一个充满活力、喜欢分享技术的29岁开发者。今天的文章,我们要来聊一聊一个发生在我们开发环境的惊险故事。这个问题折腾了我们整个团队好一阵子,最终我们发现元凶竟然是一个看似无害的@Async注解。废话不多说,直接开讲!故事的开始:微服务无法启动就在昨天,我们的开发环......
  • 一文搞定高并发编程:CompletableFuture的supplyAsync与runAsync
    CompletableFuture是Java8中引入的一个类,用于简化异步编程和并发操作。它提供了一种方便的方式来处理异步任务的结果,以及将多个异步任务组合在一起执行。CompletableFuture支持链式操作,使得异步编程更加直观和灵活。在引入CompletableFuture之前,Java已经有了Future接口来......
  • 异步实例化预制体Object.InstantiateAsync配合Async/Await使用
    Unity2022.3.20之后,可以使用异步克隆,正如前面一篇文章《Unity2022.3.20f1新功能,异步实例化预制体Object.InstantiateAsync》说明的那样,常规的使用携程方式异步克隆,但如今await/async写法如此简单方便,肯定就不想放IEnumerator里头去实现了,怎么办呢?实现一个,代码如下:publicGame......
  • 线程池以及详解使用@Async注解异步处理方法
    目录一.什么是线程池:二.使用线程池的好处:三.线程池的使用场景:四.使用线程池来提高Springboot项目的并发处理能力:1.在application.yml配置文件中配置:2.定义配置类来接受配置文件内的属性值:3.启用异步支持:4.实例: 五.详细解析@Async注解的使用:1.@Async注解作用:2.@Asyn......
  • 中文关键字检索分析-导出到csv或者excel-多文件或文件夹-使用python和asyncio和pandas
    1.02版本把原来的tab一个个拼接成文件输出,改成pandas的dataframe使用asyncio库来使用协程,但是测试下来速度好像是差不多的。可能速度太快了,没能很好的测出来差异。原来的最初的代码是java版本的,现在用python重写一遍java版本使用completableFuture来异步IO,主要是文件输......
  • Java中的异步日志记录:Logback与AsyncAppender的配置与优化
    Java中的异步日志记录:Logback与AsyncAppender的配置与优化大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java应用中,日志记录是关键的功能,但同步日志记录可能会影响性能。为了解决这个问题,异步日志记录可以显著提高应用的响应速度。本文将详细介绍......
  • 使用 `Roslyn` 分析器和修复器对.cs源代码添加头部注释
    之前写过两篇关于Roslyn源生成器生成源代码的用例,今天使用Roslyn的代码修复器CodeFixProvider实现一个cs文件头部注释的功能,代码修复器会同时涉及到CodeFixProvider和DiagnosticAnalyzer,实现FileHeaderAnalyzer首先我们知道修复器的先决条件是分析器,比如这里,如果要对代码......
  • 注解@Async失效的情况
    注解@Async失效的情况异步任务处理注解方法@Async实现异步多线程https://www.cnblogs.com/oktokeep/p/15720935.html1.注解@Async的方法不是public方法2.注解@Async的返回值只能为void或Future3.注解@Async方法使用static修饰也会失效4.spring无法扫描到异步类,没加注解@Async或......
  • Python异步编程:asyncio库详解
    \asyncio是Python的标准库,用于编写单线程的并发代码。它使用async和await语法来定义和调用异步函数,使得I/O密集型程序能够更有效地使用资源。asyncio的主要特点事件循环:asyncio程序由事件循环驱动,它负责调度协程的执行。协程:使用async定义的异步函数被称为协程。任务:asyn......
  • .net 使用IAsyncResultFilter或IResultFilter 进行restful统一风格在swagger ui中不显
    1.现实swaggerIOperationFilter 过滤器接口publicclassSwaggerOperationFilter:IOperationFilter{privatereadonlyISchemaGenerator_schemaGenerator;publicSwaggerOperationFilter(ISchemaGeneratorschemaGenerator){_schemaGenerator=......