zoukankan      html  css  js  c++  java
  • 打造静态分析器(二)基于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;
            }
        }
    
  • 相关阅读:
    ajaxpro.2.dll web.config配置
    .net中Cookie的用法
    js,cookies做悬浮购物车
    Request.UrlReferrer为空的解决
    repeater里添加序号的方法
    asp.net 母版页使用详解转
    js继承的几种实现方法
    .net用母版页和内容页实现动态Title(副标题_主标题)
    linux c数据库备份第一版
    关于sql多表去重问题
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/11687760.html
Copyright © 2011-2022 走看看