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;
            }
        }
    
  • 相关阅读:
    Eclipse 导入项目乱码问题(中文乱码)
    sql中视图视图的作用
    Java基础-super关键字与this关键字
    Android LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)的参数理解
    Android View和ViewGroup
    工厂方法模式(java 设计模式)
    设计模式(java) 单例模式 单例类
    eclipse乱码解决方法
    No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案
    【转】使用 Eclipse 调试 Java 程序的 10 个技巧
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/11687760.html
Copyright © 2011-2022 走看看