NOTE: This blog post contains code samples that work with Microsoft “Roslyn” October 2011 CTP.

While working with Roslyn, you might often come across situations where you would like to analyze and/or process certain kinds of syntax elements in the source code. This blog post gives you a couple of ways you can go about doing it.

One approach is to call the DescendentNodes method on the SyntaxNode, this returns all the nodes that descend from the current syntax node. You can call this method on the root node of the tree and then use LINQ queries to filter them based on your requirements.

For Example: If you want to obtain a list of all syntax nodes corresponding to the “do” statement in C#, the code will look like:

C#:
    var tree = SyntaxTree.ParseCompilationUnit(code);
    var doStatements = tree.Root.DescendentNodes().
        Where(x => x.Kind == SyntaxKind.DoStatement);

Similarly, if you wish to obtain a list of syntax nodes corresponding to the “do” loop in Visual Basic which has the condition expression at either the top or the bottom of the loop, the code will look like:

VB:
    Dim tree = SyntaxTree.ParseCompilationUnit(code)
    Dim doStatements = tree.Root.DescendentNodes().
        Where(Function(x)
                 Return x.Kind = SyntaxKind.DoLoopBottomTestBlock OrElse
                      x.Kind = SyntaxKind.DoLoopTopTestBlock
              End Function)

 

You can then iterate over the list of nodes and the perform the analysis and/or process them.

Depending on the kind of processing you intend on doing on the syntax nodes, another option is to implement a SyntaxWalker or SyntaxRewriter and override the methods that visit the syntax nodes that you would like to process.

If you are interesting in performing updates to a node or rewriting syntax nodes then implementing SyntaxRewriter might be more appropriate.

The code sample below show the syntax rewriter implementation for updating “do” statement syntax in C#:

C#:
 class Rewriter:SyntaxRewriter
 {
     protected override SyntaxNode VisitDoStatement(DoStatementSyntax node)
     {
         // Operate on the current node and return the updated node
     }
 }

 

The code sample below show the syntax rewriter implementation for updating “do” loops in Visual Basic:

VB:
Public Class Rewriter
    Inherits SyntaxRewriter
    Protected Overrides Function VisitDoLoopBlock(node As DoLoopBlockSyntax) As SyntaxNode
        ' Operate on the current node and return the updated node
    End Function
End Class


You can then instantiate the writer and call the visit method with the root of the tree as parameter and get back the updated root node.

C#:
        var doRewriter = new Rewriter();
        var newRoot = doRewriter.Visit(tree.Root);
VB:
        Dim doRewriter = New Rewriter()
        Dim newRoot = doRewriter.Visit(tree.Root)  

Using SyntaxRewriter you can either update the existing info on the node being processed, remove the node by returning a null or return an entirely new node that replaces the node being processed.

The code sample below illustrates how you can use SyntaxRewriter to transform a “do” statement in C# to a “while” statement by replacing the “do” node with a newly created “while” syntax node:

C#:

class Rewriter : SyntaxRewriter
{
    protected override SyntaxNode VisitDoStatement(DoStatementSyntax node)
    {
        // Operate on the current node and return the updated node
        node = (DoStatementSyntax)base.VisitDoStatement(node);

        // Get the different syntax nodes components of the Do Statement
        var doKeyword = node.DoKeyword;
        var doStatement = node.Statement;
        var whileKeyword = node.WhileKeyword;
        var condition = node.Condition;
        var openParen = node.OpenParenToken;
        var closeParen = node.CloseParenToken;
        var semicolon = node.SemicolonToken;

        // Preserve some level of trivia that was in the original Do keyword node.
        var newWhileKeyword = Syntax.Token(doKeyword.LeadingTrivia, 
            SyntaxKind.WhileKeyword, 
            whileKeyword.TrailingTrivia);

        // Preserve some level of trivia that was in the original Do keyword node and the 
        // original CloseParen token.
        var newCloseParenTrivias = closeParen.TrailingTrivia.ToList();
        newCloseParenTrivias.AddRange(doKeyword.TrailingTrivia.ToList());
        var newCloseParenTriviaList = Syntax.TriviaList(newCloseParenTrivias);
        var newCloseParen = Syntax.Token(closeParen.LeadingTrivia, 
            SyntaxKind.CloseParenToken, 
            newCloseParenTriviaList);

        var newTrailingTrivias = doStatement.GetTrailingTrivia().ToList();
        newTrailingTrivias.AddRange(semicolon.TrailingTrivia.ToList());
        var newWhileStatement = doStatement.WithTrailingTrivia(newTrailingTrivias);

        return Syntax.WhileStatement(newWhileKeyword, openParen, 
            condition, newCloseParen, newWhileStatement);
    }
}

Similarly, the code sample below illustrates how you can use SyntaxRewriter to transform a do-while loop which has the test condition on the top of the block in Visual Basic to the corresponding while loop:

VB:
Public Class Rewriter
    Inherits SyntaxRewriter
    Protected Overrides Function VisitDoLoopBlock(node As DoLoopBlockSyntax) As SyntaxNode
        node = DirectCast(MyBase.VisitDoLoopBlock(node), DoLoopBlockSyntax)
        Dim beginLoop = node.Begin
        Dim endLoop = node.End

        If (node.Kind = SyntaxKind.DoLoopTopTestBlock) AndAlso
            (beginLoop.WhileUntilClauseOpt.WhileOrUntilKeyword.Kind = SyntaxKind.WhileKeyword) Then

            Dim endKeyword = Syntax.Token(endLoop.LoopKeyword.LeadingTrivia,
                                          SyntaxKind.EndKeyword)

            Dim endWhileKeyword = Syntax.Token(Syntax.Whitespace(" "),
                                               SyntaxKind.WhileKeyword,
                                               endLoop.LoopKeyword.TrailingTrivia)

            Dim endWhile = Syntax.EndWhileStatement(endKeyword, endWhileKeyword)
            Dim beginWhileKeyword = Syntax.Token(beginLoop.DoKeyword.LeadingTrivia,
                                                 SyntaxKind.WhileKeyword,
                                                 beginLoop.DoKeyword.TrailingTrivia)

            Dim whileStatement = Syntax.WhileStatement(beginWhileKeyword,
                                                       beginLoop.WhileUntilClauseOpt.Condition)

            Return Syntax.WhileBlock(whileStatement,
                                     node.BeginTerminator,
                                     node.Statements,
                                     endWhile)
        End If

        Return node
    End Function
End Class

If you are interested in more syntax tree transformations similar to this, please do take a look at the Tree Transformation Samples that ships with the Roslyn CTP. You should be able to access the sample via the “Getting Started” link once you have installed the Roslyn CTP.