• 打造静态分析器(一)


    NCoreCoder.Aop已经写了好一段时间了,一直不温不火的,自己摸索技术也需要沉下心来深耕

    写完AOP的时候,一时感慨,纸上得来终觉浅,阅读到WebApiClient的时候,发现了一个宝贝,静态分析器~

    遂查询资料,自己打磨了一个基于NCoreCoder.Aop的静态分析器,做什么呢~代码自检啊,使用NCoreCoder.Aop的时候,增加一些自动化提示

    踩坑了几天,非常感谢walterlv(吕毅)大佬的博文以及资料,也陪我踩坑,非常感谢这个前辈

    博客园链接https://www.cnblogs.com/walterlv/ 他的最新自建博客链接 https://walterlv.com/

    分享文章,这样,我们可以自己打造自己的分析器,这个对于团队而言,增加了一定的代码检测保护

    编写前

    工欲善其事,必先利其器,我们先安装Syntax Visualizer

    参考资料https://blog.walterlv.com/post/roslyn-syntax-visualizer.html

    如果你是 Visual Studio 2017 / 2019,并且在安装 Visual Studio 时选择了 Visual Studio 扩展开发的工作负载,并且已经勾选了 .NET Compiler Platform SDK,那么你就已经安装好了。如果没有找到,请前往 如何安装和准备 Visual Studio 扩展/插件开发环境 - walterlv 再安装。如果你的 Visual Studio 版本比较旧,则需要去 .NET Compiler Platform SDK - Visual Studio Marketplace 下载安装。

    安装完之后,去“视图->其它窗口”中就可以找到“Syntax Visualizer”。

    按照好后,确认一下 视图->其他窗口,看见Syntax Visualizer

    这样就算是成功了

    上面那段引用文字要认真看,别学我,搞了半天发现找不到不对,摘抄自walterlv大佬的原文,无歧义

    我们新建一个分析器

    在已有的解决方案上,选中解决方案,单击右键添加,选择新建项目,在Extensibility选型中,选择Analyzer with Code Fix(.NET Standard)

    选择确定,会自动创建出三个项目,比如我们的项目叫Analyzer

    会自动创建出"Analyzer" "Analyzer.Test" "Analyzer.Vsix" 我们要调试分析器的话,就选择“Analyzer.Vsix”作为启动项目,运行会自动重启一个VS,这个VS用来启动我们要调试的项目

    打开AnalyzerAnalyzer.cs,删除掉自动生成的多余代码

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public class AnalyzerAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = "Analyzer";
    
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create<DiagnosticDescriptor>();
    
            public override void Initialize(AnalysisContext context)
            {
            }
        }
    

    DiagnosticAnalyzerAttribute表示要监视分析的语言

    点开LanguageNames这个静态类,发现支持C#、F#、VB?确定不是VB .Net?

    编写分析器

    打开Syntax Visualizer

    我们写一个简单的接口

        public interface IService
        {
    
        }

    鼠标选中接口,我们看看 Syntax Visualizer

    因为我们是静态分析代码,就不关注其他API了

    就关注AnalysisContext.RegisterSyntaxNodeAction即可

    编写一个分析器基类

        public abstract class BaseAnalyzContext
        {
    public abstract DiagnosticDescriptor[] SupportedDiagnostics { get; } public abstract void Execute(SyntaxNodeAnalysisContext context); }

    附上分析器代码段

        public class AnalyzerAnalyzer : DiagnosticAnalyzer
        {
            private BaseAnalyzContext[] Contexts = new BaseAnalyzContext[]
            {
                new InterfaceAnalyzerContext()
            };
    
            public const string DiagnosticId = "Analyzer";
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
    
            public AnalyzerAnalyzer()
            {
                var diagnosticDescriptors = new List<DiagnosticDescriptor>();
    
                foreach (var analyzer in Contexts)
                {
                    diagnosticDescriptors.AddRange(analyzer.SupportedDiagnostics);
                }
    
                SupportedDiagnostics = ImmutableArray.Create(diagnosticDescriptors.ToArray());
            }
    
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxNodeAction(SyntaxNodeAction, SyntaxKind.InterfaceDeclaration);
            }
    
            private void SyntaxNodeAction(SyntaxNodeAnalysisContext context)
            {
                foreach(var analyzer in Contexts)
                {
                    analyzer.Execute(context);
                }
            }
        }
    

      

      

    我们实现一个简单的接口验证,判断接口是否以大写I开头,且Interface结尾,否则抛出错误,中止

        public class InterfaceAnalyzerContext : BaseAnalyzContext
        {
            public override DiagnosticDescriptor[] SupportedDiagnostics => new DiagnosticDescriptor[]
            {
                InterfaceName
            };
            private static DiagnosticDescriptor InterfaceName = new DiagnosticDescriptor("I001", "接口验证", "接口名称错误,应该是I{0}Interface", "Error", DiagnosticSeverity.Error, True);
    
            public override void Execute(SyntaxNodeAnalysisContext context)
            {
                if (context.Node.Kind() == SyntaxKind.InterfaceDeclaration)
                {
                    var _interface = context.Node as InterfaceDeclarationSyntax;
                    var name = _interface.TryGetInferredMemberName();
    
                    if (!(name[0] == 'I' && name.EndsWith("Interface")))
                        context.ReportDiagnostic(Diagnostic.Create(InterfaceName, context.Node.GetLocation(), name));
                }
            }
        }
    

      

    把加入InterfaceAnalyzerContext加入AnalyzerAnalyzer.Contexts里面

        public class AnalyzerAnalyzer : DiagnosticAnalyzer
        {
            //。。。
            private BaseAnalyzContext[] Contexts = new BaseAnalyzContext[]
            {
                new InterfaceAnalyzerContext()
            };
            //。。。
        }
    

      

    添加一个项目,添加分析器,选中我们刚才的分析器

    我们运行起来看看

    在新开的VS里面打开我们刚才的解决方案

    打开IService.cs

    大功告成


    打个广告

    欢迎加Q群  386092459 有技术交流或分享,都非常欢迎

  • 相关阅读:
    HBuilder手机Iphone运行提示“未受信用的企业级开发者”
    在阿里云服务器ubuntu14.04运行netcore
    微信图片上传
    一段sql的优化
    设计模式之单例模式(Singleton)
    PDF.NET+EasyUI实现只更新修改的字段
    操作系统进程调度之分时,优先,分时优先
    2020最新Servlet+form表单实现文件上传(图片)
    Php7+Mysql8实现简单的网页聊天室功能
    JavaSwing+Mysql实现简单的登录界面+用户是否存在验证
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/11678761.html
Copyright © 2020-2023  润新知