打造靜態分析器(一)


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM