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 ?

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top