Using Roslyn to Analyze and Rewrite C# Syntax Trees
The .NET Compiler Platform, known as Roslyn, exposes powerful APIs
that allow developers to parse, inspect, and transform C# source code
programmatically. This guide demonstrates how to consume the Roslyn API
to parse source text into an immutable syntax tree, inspect node
structures using CSharpSyntaxWalker, and safely transform
or refactor code using CSharpSyntaxRewriter.
Prerequisites and Installation
To work with Roslyn syntax trees, install the core C# compiler package via NuGet:
dotnet add package Microsoft.CodeAnalysis.CSharp1. Parsing Code into a Syntax Tree
A syntax tree represents the lexical and syntactic structure of C# source code. Because syntax trees in Roslyn are fully fidelity and immutable, they represent every token, trivia (whitespace and comments), and syntactic construct.
To parse a raw code string into a syntax tree:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
string programText = @"
namespace SampleNamespace
{
class SampleClass
{
void SampleMethod()
{
int number = 42;
}
}
}";
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();2. Analyzing the Syntax Tree with CSharpSyntaxWalker
To inspect code without modifying it, implement a custom class that
inherits from CSharpSyntaxWalker. The walker uses the
visitor pattern to traverse nodes in the tree.
For example, to find all method declarations:
public class MethodAnalyzer : CSharpSyntaxWalker
{
public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
// Extract the method name
string methodName = node.Identifier.ValueText;
string returnType = node.ReturnType.ToString();
System.Console.WriteLine($"Found method '{methodName}' with return type '{returnType}'.");
// Continue walking child nodes
base.VisitMethodDeclaration(node);
}
}To execute the analyzer against the syntax root:
var analyzer = new MethodAnalyzer();
analyzer.Visit(root);3. Rewriting the Syntax Tree with CSharpSyntaxRewriter
Because syntax trees are immutable, nodes cannot be updated in place.
Instead, you create a new tree by replacing existing nodes.
CSharpSyntaxRewriter is a specialized visitor designed to
return modified versions of visited nodes.
The following example rewrites all integer literals by incrementing their values:
public class IncrementLiteralRewriter : CSharpSyntaxRewriter
{
public override SyntaxNode? VisitLiteralExpression(LiteralExpressionSyntax node)
{
// Check if the literal is a numeric integer
if (node.IsKind(SyntaxKind.NumericLiteralExpression) && node.Token.Value is int value)
{
// Create a new literal token with incremented value
var newToken = SyntaxFactory.Literal(
node.Token.LeadingTrivia,
(value + 1).ToString(),
value + 1,
node.Token.TrailingTrivia
);
// Return the updated node
return SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, newToken);
}
return base.VisitLiteralExpression(node);
}
}4. Applying the Rewriter and Generating Code
To apply the transformation, pass the root node to the rewriter and retrieve the new tree:
var rewriter = new IncrementLiteralRewriter();
SyntaxNode newRoot = rewriter.Visit(root);
// Output the updated source code
string modifiedCode = newRoot.ToFullString();
System.Console.WriteLine(modifiedCode);The resulting modifiedCode string preserves the
formatting and whitespace of the original source text while replacing
int number = 42; with int number = 43;.