Introduction
In the previous article, we compared Visual Studio's templates that one can use to build their own static analyzer. There we detailed how to write your first simple analyzer and talked about how Roslyn maps source code as syntax trees. Now we will talk about syntax tree structure in more detail. You will learn more about using the CSharpSyntaxWalker class to traverse syntax tree nodes.
Syntax Trees
We have already touched upon the syntax tree concept in the previous article. Let's delve deeper into the topic and talk more about syntax trees. As you already know, syntax trees are built based on source code. Each .cs source code file has its own syntax tree. One way to look at a syntax tree is to use the Syntax Visualizer window. If you've already installed the .NET Compiler Platform SDK for your VS, use the following path to access this window: View -> Other Windows -> Syntax Visualizer.
if (number > 0)
{
}
- Syntax nodes (blue) - syntax tree nodes;
- Syntax tokens (green) - tokens;
- Syntax trivia (white and gray) - various additional syntax information.
Syntax Nodes
- IfStatementSyntax – the if statement;
- InvocationExpressionSyntax – a method call;
- ReturnStatementSyntax – the return operator;
- MemberAccessExpressionSyntax – access to structure/class members.
- ChildNodes - gets the current node's child nodes.
- DescendantNodes - gets a list of all nodes that are the current node's descendants.
- Contains - checks whether the current node includes another node, which is passed as an argument;
- IsKind - takes a SyntaxKind enumeration element as a parameter and returns a boolean value. Determines whether a particular node type matches the type of the node passed as an argument.
Syntax Tokens
Syntax Trivia
How to Create a Diagnostic Rule: Tree Nodes
public static void MyFunc1(int count) { if (count > 100) { Console.WriteLine("Hello world!"); } else { Console.WriteLine("Hello world!"); } }
public static Project GetProjectFromSolution(String solutionPath) { MSBuildLocator.RegisterDefaults(); MSBuildWorkspace workspace = MSBuildWorkspace.Create(); Solution currSolution = workspace.OpenSolutionAsync(solutionPath) .Result; return currSolution.Projects.Single(); }
public static StringBuilder warnings = new StringBuilder();
static void Main(string[] args) { string solutionPath = @"D:\Test\TestApp.sln"; string logPath = @"D:\Test\warnings.txt"; Project project = GetProjectFromSolution(solutionPath); foreach (var document in project.Documents) { var tree = document.GetSyntaxTreeAsync().Result; var ifStatementNodes = tree.GetRoot() .DescendantNodesAndSelf() .OfType<IfStatementSyntax>(); foreach (var ifStatement in ifStatementNodes) { if (ApplyRule(ifStatement)) { int lineNumber = ifStatement.GetLocation() .GetLineSpan() .StartLinePosition.Line + 1; warnings.AppendLine($"'if' with equal 'then' and " + $"'else' blocks is found in file" + $" {document.FilePath} at line" + $" {lineNumber}"); } } } if (warnings.Length != 0) File.AppendAllText(logPath, warnings.ToString()); }
public static bool ApplyRule(IfStatementSyntax ifStatement) { if (ifStatement.Else == null) return false; StatementSyntax thenBody = ifStatement.Statement; StatementSyntax elseBody = ifStatement.Else.Statement; return SyntaxFactory.AreEquivalent(thenBody, elseBody); }
How to Create a Diagnostic Rule: CSharpSyntaxWalker
public class IfWalker : CSharpSyntaxWalker { public override void VisitIfStatement(IfStatementSyntax node) { if (ApplyRule(node)) { int lineNumber = node.GetLocation() .GetLineSpan() .StartLinePosition.Line + 1; warnings.AppendLine($"'if' with equal 'then' and " + $"'else' blocks is found in file" + $" {node.SyntaxTree.FilePath} at line" + $" {lineNumber}"); } base.VisitIfStatement(node); } }
public static void StartWalker(SyntaxNode syntaxNode) { var walker = new IfWalker(); walker.Visit(syntaxNode); }
static void Main(string[] args) { string solutionPath = @"D:\Test\TestApp.sln"; string logPath = @"D:\Test\warnings.txt"; Project project = GetProjectFromSolution(solutionPath); foreach (var document in project.Documents) { warnings.Clear(); var tree = document.GetSyntaxTreeAsync().Result; StartWalker(tree.GetRoot()); if (warnings.Length != 0) File.AppendAllText(logPath, warnings.ToString()); } }
Summary
Credits
Article Type : | Guest Article |
Author : | Ilya Gainulin |
Tags : | CSharp, Knowledge |
Article Date : | 26-02-2021 |
Article Publish Date : | 07-04-2021 |
Note : All content of this article are copyright of their author. |