上一篇,我們打造了一個簡單的分析器,但是我們實際使用分析器就是為了對項目做分析檢測,增加一些非語法的自檢的
比如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;
}
}
