• 打造静态分析器(二)基于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;
            }
        }
    
  • 相关阅读:
    clickhouse 多数据源
    maven-dependency-plugin maven-assembly-plugin
    maven shade plugin
    远程服务器,无法复制粘贴 (通过mstsc复制粘贴失败需要重新启动RDP剪切板监视程序rdpclip.exe)
    Mysql导入大sql文件方法
    MySQL5.7更新json类型字段中的某个key的值 函数json_replace()
    mysq json类型
    增强mybatis-plus的typeHandler,可以支持List<T> 中嵌套对象
    Windows中查看端口占用及关闭对应进程
    Hibernate中继承体现
  • 原文地址:https://www.cnblogs.com/NCoreCoder/p/11687760.html
Copyright © 2020-2023  润新知