4

已解决: mike z 是对的,我没有正确调用基础来继续递归。谢谢,迈克

我正在使用 Roslyn 进行一些代码重写,通过实现一个SyntaxRewriter.

我遇到的奇怪的事情是,当覆盖时SyntaxNode.VisitInvocationExpression(InvocationExpressionSyntax),它不会访问InvocationExpressionSyntax树中的所有节点。(我认为所有SyntaxNode类型都相同)

例如,给定这个调用表达式:

  controller.Add(5, 6).ToString();

它只访问整个表达式的节点,即使那里有 2 次调用。

虽然我当然可以编写递归函数或类似函数来解析子/嵌套 InvocationExpression 节点,但这似乎不一致且不方便。
为什么不访问整个树中所有 * 类型的节点?

这是我的覆盖:

    public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
    {
        IdentifierNameSyntax ident = node.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault();
        if (ident == null)
            return node;//In my test case, the example above returns here when it's node is encountered.  Shouldn't this then allow the walker to continue deeper into the node,
                        // finding the deeper nested Invocations?

        string name = ident.PlainName;
        if (!TempStore.ConstructedInvocations.ContainsKey(name))//not replacing this then
            return node;

        InvocationExpressionSyntax newInvocation = ((InvocationExpressionSyntax)TempStore.ConstructedInvocations[name]).WithArgumentList(node.ArgumentList);
        return newInvocation;
    }

在调试中单步执行该代码确认InvocationExpressionNodeforcontroller.Add(5, 6).ToString();确实有子InvocationExpressionNodes嵌套在里面。

4

2 回答 2

6

我正在使用 Roslyn API 并且遇到了类似的问题。在 Visit* 方法的基类实现中递归调用 Visit 方法。一旦您覆盖其中之一,您将负责访问所有子节点。您可以通过在要重写的节点上调用 base.Visit* 方法或在每个子节点上调用 Visit 来执行此操作。

这是一些示例代码,我通过交换它们来重写逻辑运算符 && 和 ||。我用不同的运算符构造了一个新节点,然后调用 base.VisitBinaryExpression 以确保访问所有子节点。否则,我们只会重写表达式的一部分,例如var1 && (var2 || var3). 另一种可能的实现是在 node.Left 和 node.Right 上调用 Visit,然后根据这些结果构造新节点。

public class LogicalOperatorRewriter : SyntaxRewriter
{
    public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
    {
        SyntaxKind newExpressionKind = GetNewKind(node.Kind);
        BinaryExpressionSyntax newNode = (BinaryExpressionSyntax)Syntax.BinaryExpression(newExpressionKind, left: node.Left, right: node.Right).Format().GetFormattedRoot();
        return base.VisitBinaryExpression(newNode);
    }

    private SyntaxKind GetNewKind(SyntaxKind kind)
    {
        switch (kind)
        {
            case SyntaxKind.LogicalAndExpression:
                return SyntaxKind.LogicalOrExpression;
            case SyntaxKind.LogicalOrExpression:
                return SyntaxKind.LogicalAndExpression;
            default: return kind;
        }
    }
}
于 2012-07-29T19:27:49.167 回答
3

我不知道 Roslyn API,但使用表达式树 ( ExpressionVisitor),您需要调用基本方法才能继续访问树。

这是因为您的方法可能希望停止访问树。如果你想停止,你可以不调用基本方法。

这个示例代码似乎支持这个理论。

于 2012-07-29T14:33:17.523 回答