打造靜態分析器(二)基於Asp.Net Core 3.0的AspectCore組件檢測


上一篇,我們打造了一個簡單的分析器,但是我們實際使用分析器就是為了對項目做分析檢測,增加一些非語法的自檢的

比如Asp.Net Core 3.0的替換依賴注入檢測

設計分析

我們創建一個默認的Asp.Net Core 3.0的項目

打開Startup.cs

大致結構如下

 

 

我們要針對Startup分析,第一方法ConfigureServices的返回類型必須是void

這是Asp.Net Core 3.0的改動之一,所以,當Startup.ConfigureServices返回類型不等於void的時候,我們就拋出錯誤提示

我們編寫一個Startup的監視代碼

    public class StartupAnalyzerContext : BaseAnalyzContext
    {
        private static DiagnosticDescriptor NotFindConfigureServices = new DiagnosticDescriptor("Class", "Startup", "未找到方法ConfigureServices", "Error", DiagnosticSeverity.Error, true);
        public override DiagnosticDescriptor[] SupportedDiagnostics => new DiagnosticDescriptor[]
        {
            NotFindConfigureServices
        };

        public override void Execute(SyntaxNodeAnalysisContext context)
        {
            if (context.Node.Kind() == SyntaxKind.ClassDeclaration &&
                context.Node is ClassDeclarationSyntax classDeclaration &&
                classDeclaration.Identifier.Text.Equals("Startup")
            )
            {
                var methods = classDeclaration.Members.OfType<MethodDeclarationSyntax>();

                if (!methods.Any(_method => _method.Identifier.Text.Equals("ConfigureServices")))
                    context.ReportDiagnostic(Diagnostic.Create(NotFindConfigureServices, classDeclaration.GetLocation()));
                else
                {

                }
            }
        }
    }

  

如果沒在Startup類里找到ConfigureServices方法則拋出錯誤

我們注釋掉ConfigureServices方法

看看結果

 

 

已經可以正常分析方法了

 

下一步,我們分析ConfigureServices方法的返回值是否是void

                else
                {
                    var voidSymbol = context.Compilation.GetTypeByMetadataName(typeof(void).FullName);

                    var configureServices = methods.FirstOrDefault(_method => _method.Identifier.Text.Equals("ConfigureServices"));

                    var returnType = configureServices.ReturnType;
                    var typeInfo = context.SemanticModel.GetTypeInfo(returnType);

                    if (!typeInfo.Type.Equals(voidSymbol))
                        context.ReportDiagnostic(Diagnostic.Create(ConfigureServicesReturnType, configureServices.GetLocation()));
                }

  

我們剛才的else部分邏輯,找到了ConfigureServices方法才進入這部分邏輯,我們修改一下ConfigureServices方法返回值,改為Asp.Net Core 3.0一下,常見的IServiceProvider

 

 

 

完善AspectCore的依賴注入替換分析

項目引用AspectCore.Extensions.DependencyInjection

判斷Program.CreateHostBuilder是否存在

分析CreateHostBuilder的代碼體,是否存在一個UseServiceProviderFactory方法,存在則判斷是否是泛型AspectCore.Injector.IServiceContainer的類

    public class ProgramAnalyzerContext : BaseAnalyzContext
    {
        private static DiagnosticDescriptor NotFindCreateHostBuilder = new DiagnosticDescriptor("Class", "Program", "未找到方法CreateHostBuilder", "Error", DiagnosticSeverity.Error, true);
        private static DiagnosticDescriptor CreateHostBuilderReturnType = new DiagnosticDescriptor("Class", "Program", "無法分析返回值類型非IHostBuilder的CreateHostBuilder方法", "Error", DiagnosticSeverity.Error, true);
        private static DiagnosticDescriptor NotFindUseServiceProviderFactory = new DiagnosticDescriptor("Class", "Program", "未找到UseServiceProviderFactory方法", "Error", DiagnosticSeverity.Error, true);
        private static DiagnosticDescriptor AspectCoreServiceProviderFactory = new DiagnosticDescriptor("Class", "Program", "請Nuget安裝AspectCore.Extensions.DependencyInjection", "Error", DiagnosticSeverity.Error, true);
        private static DiagnosticDescriptor UseServiceProviderFactoryType = new DiagnosticDescriptor("Class", "Program", "UseServiceProviderFactory(new AspectCoreServiceProviderFactory())", "Error", DiagnosticSeverity.Error, true);

        public override DiagnosticDescriptor[] SupportedDiagnostics => new DiagnosticDescriptor[]
        {
            NotFindCreateHostBuilder,
            CreateHostBuilderReturnType,
            NotFindUseServiceProviderFactory,
            AspectCoreServiceProviderFactory,
            UseServiceProviderFactoryType
        };

        public override void Execute(SyntaxNodeAnalysisContext context)
        {
            if (context.Node.Kind() == SyntaxKind.ClassDeclaration &&
                context.Node is ClassDeclarationSyntax classDeclaration &&
                classDeclaration.Identifier.Text.Equals("Program")
            )
            {
                var methods = classDeclaration.Members.OfType<MethodDeclarationSyntax>();

                if (!methods.Any(_method => _method.Identifier.Text.Equals("CreateHostBuilder")))
                    context.ReportDiagnostic(Diagnostic.Create(NotFindCreateHostBuilder, classDeclaration.GetLocation()));
                else
                {
                    var aspectCoreServiceProviderFactorySymbol = context.Compilation.GetTypeByMetadataName("AspectCore.Extensions.DependencyInjection.AspectCoreServiceProviderFactory");
                    var iServiceContainerSymbol = context.Compilation.GetTypeByMetadataName("AspectCore.Injector.IServiceContainer");

                    if (aspectCoreServiceProviderFactorySymbol == null)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(AspectCoreServiceProviderFactory, classDeclaration.GetLocation()));

                        return;
                    }

                    var createHostBuilder = methods.FirstOrDefault(_method => _method.Identifier.Text.Equals("CreateHostBuilder"));

                    var expressionBody = createHostBuilder.ExpressionBody as ArrowExpressionClauseSyntax;

                    if (expressionBody != null)
                    {
                        var expressions = ConvertArrowExpression(expressionBody);

                        if (!expressions.Any(expression=> ((MemberAccessExpressionSyntax)expression.Expression).Name.Identifier.Text.Equals("UseServiceProviderFactory")))
                            context.ReportDiagnostic(Diagnostic.Create(NotFindUseServiceProviderFactory, createHostBuilder.GetLocation()));
                        else
                        {
                            var useServiceProviderFactoryExpression = expressions.FirstOrDefault(expression => ((MemberAccessExpressionSyntax)expression.Expression).Name.Identifier.Text.Equals("UseServiceProviderFactory"));                               
                            var method = context.SemanticModel.GetSymbolInfo(useServiceProviderFactoryExpression.Expression).Symbol as IMethodSymbol;

                            if (!method.TypeArguments.Any(_param => _param.Equals(iServiceContainerSymbol)))
                                context.ReportDiagnostic(Diagnostic.Create(UseServiceProviderFactoryType, createHostBuilder.GetLocation()));
                        }
                    }
                }
            }
        }

        private List<InvocationExpressionSyntax> ConvertArrowExpression(ArrowExpressionClauseSyntax expresionBody)
        {
            var result = new List<InvocationExpressionSyntax>();
            var firstExpresson = (InvocationExpressionSyntax) expresionBody.Expression;
            var expression = firstExpresson;

            result.Add(expression);

            while (IsNext(expression))
            {
                expression = Next(expression);
                result.Add(expression);
            }

            return result;
        }

        private InvocationExpressionSyntax Next(InvocationExpressionSyntax expression)
        {
            var method = (MemberAccessExpressionSyntax) expression.Expression;

            Console.WriteLine($"{method.Name}");

            return (InvocationExpressionSyntax)method.Expression;
        }

        private bool IsNext(InvocationExpressionSyntax expression)
        {
            var method = (MemberAccessExpressionSyntax)expression.Expression;

            return method.Expression.Kind() == SyntaxKind.InvocationExpression;
        }
    }

 


免責聲明!

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



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