打造静态分析器(一)

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 有技术交流或分享,都非常欢迎

posted @ 2019-10-15 16:52  沉迷代码的萌新  阅读(789)  评论(0编辑  收藏  举报