为什么我的 Roslyn 分析器不报告警告?

问题描述

我正在编写一个 Roslyn 分析器来检查 JSON 反序列化的目标类型是否遵循一些规则。本质上,如果表达式 Foo 中出现类型 JsonConvert.DeserializeObject<Foo>,那么它应该是一个简单的 DTO(认构造函数,具有 JSON 友好类型的公共属性)。

我已经注册一个语法节点操作来触发调用表达式。找到合适的调用工作得很好。然后我继续使用语义模型从泛型方法调用获取类型符号(在本例中为 Foo)。最后,我使用类型符号对 Foo 类型进行检查。到现在为止还挺好。问题是当我想报告警告时。如果我在对 SyntaxNodeAnalysisContext.ReportDiagnostic调用中使用调用表达式的位置,它会起作用:我的警告出现,并且我在调用下得到一个波浪线。但这不是我想要的。我希望波浪线出现在 Foo 的声明中,例如在有问题的属性或非认构造函数下。但是,这似乎不起作用。警告根本没有出现在警告列表中,也没有波浪线。由于某种原因,报告似乎被忽略或静失败。

我尝试了多种方法获取要使用的适当位置:

  • 直接使用符号的Locationssymbol.Locations[0]
  • 在源代码树上使用 FindNode 查找符号的 SyntaxNodesymbolLocation.sourceTree?.GetRoot()?.FindNode(symbolLocation.sourceSpan)?.GetLocation()
  • 使用 DeclaringSyntaxReferencessymbol.DeclaringSyntaxReferences[0].GetSyntax().GetLocation()

所有这些方法都会产生一个位置(它不为空)。但是我的警告没有出现。

代码如下:

private void DoAnalyzeInvocationExpression(SyntaxNodeAnalysisContext context)
{
    var invocation = (InvocationExpressionSyntax)context.Node;

    if (invocation.Expression is MemberAccessExpressionSyntax memberAccess)
    {
        if (memberAccess.Expression is IdentifierNameSyntax typeIdentifierSyntax)
        {
            var textIdentifierText = typeIdentifierSyntax.Identifier.Text;
            if (textIdentifierText == "JsonConvert")
            {
                if (memberAccess.Name is GenericNameSyntax genericNameSyntax)
                {
                    if (genericNameSyntax.Identifier is SyntaxToken SyntaxToken)
                    {
                        if (SyntaxToken.Text == "DeserializeObject")
                        {
                            var typeArgs = genericNameSyntax.TypeArgumentList.Arguments;
                            if (typeArgs.Count == 1)
                            {
                                var typeArg = typeArgs[0];
                                var symbolInfo = context.SemanticModel.GetSymbolInfo(typeArg);
                                var typeSymbol = (INamedTypeSymbol)symbolInfo.Symbol;
                                var incompatibilities = VerifyDeserializationType(typeSymbol);
                                var diagnostics = incompatibilities.Select(it => ToSyntaxNodeAnalysisDiagnostic(it,memberAccess));
                                if (diagnostics.Any())
                                {
                                    // This works,and gives an overall warning that the deserialization has problems.
                                    context.ReportDiagnostic(Diagnostic.Create(DeserializeRule,invocation.GetLocation(),typeSymbol.Name));
                                    try
                                    {
                                        foreach (var d in diagnostics)
                                        {
                                            // These don't work,presumably because there is something wrong with the location.
                                            context.ReportDiagnostic(d);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        // This doesn't happen.
                                        context.ReportDiagnostic(Diagnostic.Create(DebugRule,memberAccess.GetLocation(),$"EXCEPTION: {ex.Message}"));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

private static Diagnostic ToSyntaxNodeAnalysisDiagnostic(Incompatibility incompatibility,SyntaxNode sourceNode)
{
    var symbolLocation = incompatibility.Symbol.Locations[0];

    var SyntaxRef = incompatibility.Symbol.DeclaringSyntaxReferences[0];
    var Syntax = SyntaxRef.GetSyntax();

    //var node = symbolLocation.sourceTree?.GetRoot()?.FindNode(symbolLocation.sourceSpan);

    var sourceLocation = sourceNode.GetLocation();

    if (Syntax == null)
    {
        return Diagnostic.Create(DebugRule,sourceLocation,"node == null");
    }

    var location = Syntax.GetLocation();
    if (location == null)
    {
        return Diagnostic.Create(DebugRule,"location == null");
    }

    if (incompatibility is PropIncompatibility propIncompatibility)
    {
        return Diagnostic.Create(PropRule,location,propIncompatibility.Symbol.Name,propIncompatibility.Explanation);
    }

    if (incompatibility is CtorIncompatibility)
    {
        return Diagnostic.Create(CtorRule,incompatibility.Symbol.ContainingSymbol.Name);
    }

    return Diagnostic.Create(DebugRule,"Unhandled incompatibility...");
}

欢迎提出任何建议。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)