打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

上一篇,我们打造了一个简单的分析器,但是我们实际使用分析器就是为了对项目做分析检测,增加一些非语法的自检的

比如Asp.Net Core 3.0的替换依赖注入检测

设计分析

我们创建一个默认的Asp.Net Core 3.0的项目

打开Startup.cs

大致结构如下

打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

我们要针对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方法

看看结果

打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

已经可以正常分析方法了

下一步,我们分析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

打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

完善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;
}
}
上一篇:Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用


下一篇:修改版: 小伙,多线程(GCD)看我就够了,骗你没好处!