Roslyn 分析器应同时使用多个代码文件

问题描述 投票:0回答:1

我目前正在编写一些 Roslyn 分析器,其中一些必须使用来自多个类的信息(通常位于不同的文件中)才能完成其工作。

例如,我需要一个分析器,检查一个类是否实现了一个接口。如果是这种情况,分析器将搜索“HandleAsync”方法并检查返回类型。返回类型应该是通用的,如果是这种情况,分析器会尝试检查通用类(例如 ValueTask 的 T)是否具有特定属性。

我尝试了不同的方法,但结果总是相同的:它在我的测试中有效,因为这些类位于同一个文件中。在实际应用中,分析仪不起作用。

public override void Initialize(AnalysisContext context)
{
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.EnableConcurrentExecution();
    context.RegisterCompilationAction(CheckForGenericReturnType);
}

private void CheckForGenericReturnType(CompilationAnalysisContext context)
{
    var compilation = context.Compilation;
    foreach (var syntaxTree in compilation.SyntaxTrees) 
    { 
        var semanticModel = compilation.GetSemanticModel(syntaxTree);

        var root = syntaxTree.GetRoot();
        var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>();

        foreach (var classDeclaration in classDeclarations)
        {
            var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;

            var name = semanticModel.GetDeclaredSymbol(classDeclaration).BaseType.Name;

            if (!name.Equals("SomeInterface"))
            {
                continue;
            }

            var methodSymbol = classSymbol.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(method => method.Name == "HandleAsync");

            if (methodSymbol == null)
            {
                continue;
            }

            var returnType = methodSymbol.ReturnType;

            if (returnType != null)
            {
                if (returnType is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType)
                {
                    var genericClassName = namedTypeSymbol.TypeArguments.FirstOrDefault();

                    if (genericClassName != null)
                    {
                        // search for classDefinition of the generic class, than check if attribute exists.
                        CheckForGenericClass(context, genericClassName);
                    }
                }
            }
        }
    }    
}

private void CheckForGenericClass(CompilationAnalysisContext context, ITypeSymbol className)
{
    var compilation = context.Compilation;
    foreach (var syntaxTree in compilation.SyntaxTrees)
    {
        var semanticModel = compilation.GetSemanticModel(syntaxTree);

        var root = syntaxTree.GetRoot();
        var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>();

        foreach (var classDeclaration in classDeclarations)
        {
            var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;

            var name = semanticModel.GetDeclaredSymbol(classDeclaration).Name;

            if (!name.Equals(className.Name))
            {
                continue;
            }

            var hasAttribute = classSymbol.GetAttributes().Any(attribute => attribute.AttributeClass.Name == "TestAttribute");

            if (!hasAttribute)
            {
                var diagnostic = Diagnostic.Create(Rule, classSymbol.Locations[0], classSymbol.Name);
                context.ReportDiagnostic(diagnostic);
            }
            return;
        }
    }
}
c# .net compilation roslyn roslyn-code-analysis
1个回答
0
投票

这里的问题是,

RegisterCompilationAction
是用于语法分析的,你需要注册一个用于语义分析的动作(如
RegisterSyntaxNodeAction
)来获取真实类型符号。

例如:

context.RegisterSyntaxNodeAction(CheckForGenericReturnType,
                                 SyntaxKind.MethodDeclaration);

这会注册每个方法声明要执行的动作,你可以从上下文中获取一个

IMethodSymbol
,然后你可以检查它的名称、接口等

private void CheckForGenericReturnType(SyntaxNodeAnalysisContext context)
{
    var symbol = context.SemanticModel.GetDeclaredSymbol(context.Node);

    if (symbol is IMethodSymbol methodSymbol &&

        symbol.Name == "HandleAsync" &&

        symbol.ContainingType.Interfaces.Any(i => i.Name == "SomeInterface") &&

        methodSymbol.ReturnType is INamedTypeSymbol namedReturnType &&

        namedReturnType.IsGenericType &&

        namedReturnType.TypeArguments.First()
                       .GetAttributes().First()
                       .AttributeClass.Name == "TestAttribute"
        ...
© www.soinside.com 2019 - 2024. All rights reserved.