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;
    }

デバッグでそのコードをステップ実行すると、InvocationExpressionNodeforの内部controller.Add(5, 6).ToString();に子が実際にInvocationExpressionNodesネストされていることが確認されます。

4

2 に答える 2

6

私は Roslyn API で遊んでいましたが、同様の問題がありました。Visit メソッドは、Visit* メソッドの基本クラスの実装で再帰的に呼び出されています。これらのいずれかをオーバーライドすると、すべての子ノードにアクセスする責任があります。これを行うには、書き換えるノードで base.Visit* メソッドを呼び出すか、各子ノードで Visit を呼び出します。

以下は、論理演算子 && と || を入れ替えて書き直したサンプル コードです。別の演算子を使用して新しいノードを作成し、base.VisitBinaryExpression を呼び出して、すべての子ノードがアクセスされるようにします。そうでなければ、 のような式の一部だけを書き換えvar1 && (var2 || var3)ます。もう 1 つの可能な実装は、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)を使用している場合は、ベースメソッドを呼び出してツリーをたどり続ける必要があります。

これは、メソッドがツリーの下へのアクセスを停止したい場合があるためです。停止したい場合は、baseメソッドを呼び出すことはできません。

このサンプルコードは、この理論をサポートしているようです。

于 2012-07-29T14:33:17.523 に答える