Why?
In the previous post we talked about Roslyn and how it helps us to have extended interaction with the C# Compiler, that being said, there are situations that we need to generate some code as source generators or when we are creating a code analyzer and want to have a code fixer for that, we need to create or change a syntax tree. The syntax tree is the result of the first phase, Parser, in the pipeline.
The image below is the syntax tree representing the following code, I have used SharpLab.io to get it, it is a tool that every C# developer should be familiar with, it could show the Compiler generated C# code, IL code, or the Syntax Tree representing the code.
using System;
Console.WriteLine("🌄");

The syntax tree contains of various type of nodes, the blue ones in the image are SyntaxNode
, they are abstract classes that could contain other types of nodes, the yellow ones (if you have visual studio and using Syntax Tree Visualizer, they are green) are SyntaxToken
, they are structs and are the termination in the tree, even though SyntaxTrivia
struct type are attached to them, the trivias do not contribute to the semantic of the program, but to the formatting and layout of it. Syntax tokens are things like identifiers, keywords, etc. Well, now to create a syntax tree there are two approaches (at least that I am aware of it 😉) one could leverage.
Syntax Tree - Lets create one
The first and easier approach is to use a string and the the methods from factory methods, SyntaxFactory.ParseSyntaxTree
, and CSharpSyntaxTree.ParseText
:
const string sourceCode = """
using System;
namespace BuildingCode;
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Sir Ta Piaz!");
}
}
""";
// 1. Create a Syntax Tree
var syntaxTree1 = SyntaxFactory.ParseSyntaxTree(sourceCode);
var syntaxTree2 = CSharpSyntaxTree.ParseText(sourceCode);
The second way to create a syntax tree is using other factory methods provided by the SyntaxFactory
class, since the syntax tree is Immutable, every call returns a new version of the previous one and not changing it. Writing all of this code by hand might be tedious and cumbersome, therefore, I used another tool that every C# developer working with Roslyn should be aware of: Roslyn Quoter
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
var compilationUnitSyntaxNode =
CompilationUnit()
.WithUsings(
SingletonList<UsingDirectiveSyntax>(
UsingDirective(
IdentifierName("System"))))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
GlobalStatement(
ExpressionStatement(
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("Console"),
IdentifierName("WriteLine")))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList<ArgumentSyntax>(
Argument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal("🌄"))))))))))
.NormalizeWhitespace()
That's it! There are two more things I would like to mention in here, 1. the root node of the tree is CompilationUnit
, it is kind of representing a C# file, and contains high level nodes like using directives, namespaces, global statements, type declarations, etc. Having a CompilationUnitSyntax
at the root gives us a consistent structure for the API knowing where to look if you want to investigate top-level code components, also, a single entry point guarantees we can traverse all constructs in the file without missing anything! 2. the last line of code is a call to NormalizeWhitespace
, this just applies the common formatting to the code by adding some trivia where necessary.
Conclusion
There are a couple of ways to generate a syntax tree in C#, using SyntaxFactory.ParseSyntaxTree
and CSharpSyntaxTree.ParseText
, or using other factory methods provided by the SyntaxFactory
class, choose your weapon as required 😁 Remember, a tree has knowledge about the textual content of the tree's information and how that content relates to C#, with CompilationUnit
being the root of the tree representing a C# file. This design allows developers to reliably write analyzers, refactoring, and source generators that operate on a guaranteed structure, simplifying the process of parsing, inspecting, and modifying C# code. Do not forget, that everything in Roslyn is Immutable, be careful with assigning your variables because it is easy to forget about this aspect.
Thanks for reading so far, enjoy coding and Dametoon Garm [1] 😊