Roslyn doubts on SyntaxTree construction
Question
Disclaimer
I'm pretty sure I'm missing something obvious, but even after reading official documentation I don't clearly understand how Roslyn create a syntax tree.
Example
Consider the following, simple code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace aopsample
{
class UsbReadWriter
{
public bool ReadFromUsb()
{
return true;
}
public virtual bool WriteToUsb()
{
return true;
}
}
}
I get a SyntaxTree
for this code and make something like this, very rough and simple, but I just need to understand.
string[]lines = System.IO.File.ReadAllLines(filename);
string tocompile = string.Join(string.Empty, lines);
SyntaxNode root = tree.GetRoot(new CancellationToken());
foreach (SyntaxNode sn in root.ChildNodes())
{
if (sn.Kind == SyntaxKind.NamespaceDeclaration)
{
//I get a namespace, so it's Child node just will be class
foreach (SyntaxNode sname in sn.ChildNodes())
{
if (sname.Kind == SyntaxKind.ClassDeclaration)
{
//I get class, so it's Children will be methods of the class
foreach (SyntaxNode sclass in sname.ChildNodes()) // **{1}**
{
if (sclass.Kind == SyntaxKind.MethodDeclaration)
{
}
}
}
}
And it works pretty well.
Trouble
But, it's enough to add a comment to the ReadFromUsb()
method, something like this
/// <summary>
/// Reads a data from Usb
/// </summary>
/// <returns></returns>
public bool ReadFromUsb()
{
return true;
}
And ChildNodes()
call on {1} marked line, for CLASS (???) returns 0.
Question
Why adding a comment to member function, resets the collection of parent CLASS children Syntax nodes ?
What am I missing ?
Solution
Following a chat discussion, we determined that the problem was the the code to parse was being constructed with:
string[]lines = System.IO.File.ReadAllLines(filename);
string tocompile = string.Join(string.Empty, lines);
Which puts all of the code onto a single line. Therefore everything after //
becomes a comment. The solution is just to use Environment.NewLine
as the join character, or use ReadAllText
instead of ReadAllLines
.
OTHER TIPS
Since comments can appear anywhere at all in source code, they are not modeled as a ChildNode
, which are reserved for true syntactic elements. Instead, they are considered SyntaxTrivia
. In your example, you should be able to look at the LeadingTrivia
of the method and see the comment.
Additionally, since this is an XML doc comment which may have interesting structure of it's own, that will be modeled as it's own little tree that you can get with the GetStructure()
method of the SyntaxTrivia
.
For such situations, Using CSharpSyntaxWalker
can be of great help.
Following is sample extracted from Josh Varty's blog here which helps to print syntax tree on console :
public class CustomWalker : CSharpSyntaxWalker
{
static int Tabs = 0;
public override void Visit(SyntaxNode node)
{
Tabs++;
var indents = new String('\t', Tabs);
Console.WriteLine(indents + node.Kind());
base.Visit(node);
Tabs--;
}
}
static void Main(string[] args)
{
var tree = CSharpSyntaxTree.ParseText(@"
public class MyClass
{
public void MyMethod()
{
}
public void MyMethod(int n)
{
}
");
var walker = new CustomWalker();
walker.Visit(tree.GetRoot());
}
You can also print trivias by calling GetLeadingTrivia
and GetTrailingTrivia
methods on syntax nodes.