Рослин сомневается в построении SyntaxTree

Отказ от ответственности

Я почти уверен, что упускаю что-то очевидное, но даже после прочтения официальной документации я не совсем понимаю, как Roslyn создает синтаксическое дерево.

Пример

Рассмотрим следующий простой код:

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;
        }
    }
}

Я получаю SyntaxTree за этот код и делаю что-то вроде этого, очень грубо и просто, но мне просто нужно понять.

 

  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) 
                     {
                        
                     }
                 }
             }
        }

И это работает очень хорошо.

Беда

Но, достаточно добавить к методу ReadFromUsb() комментарий, примерно такой

/// <summary>
/// Reads a data from Usb
/// </summary>
/// <returns></returns>
public bool ReadFromUsb()
{
    return true;
}

И вызов ChildNodes() на отмеченной строке {1} для CLASS (???) возвращает 0.

Вопрос

Почему добавление комментария к функции-члену сбрасывает коллекцию родительских дочерних узлов синтаксиса CLASS?

Что мне не хватает?


person Tigran    schedule 30.12.2011    source источник
comment
В качестве примечания: я думаю, что использование LINQ сделало бы код zour более понятным. И я думаю, что это в целом относится к выбору узлов в Roslyn.   -  person svick    schedule 31.12.2011
comment
@svick: да, хорошо. Как я уже говорил, этот код просто быстрый и грязный.   -  person Tigran    schedule 31.12.2011


Ответы (3)


После обсуждения в чате мы определили, что проблема заключалась в том, что код для синтаксического анализа создавался с помощью:

string[]lines = System.IO.File.ReadAllLines(filename); 
string tocompile = string.Join(string.Empty, lines);

Что помещает весь код в одну строку. Поэтому все после // становится комментарием. Решение состоит в том, чтобы просто использовать Environment.NewLine в качестве символа соединения или использовать ReadAllText вместо ReadAllLines.

person Kevin Pilch    schedule 30.12.2011

Поскольку комментарии могут появляться где угодно в исходном коде, они не моделируются как ChildNode, которые зарезервированы для настоящих синтаксических элементов. Вместо этого они считаются SyntaxTrivia. В вашем примере вы должны увидеть LeadingTrivia метода и комментарий.

Кроме того, поскольку это комментарий XML-документа, который может иметь собственную интересную структуру, он будет смоделирован как собственное маленькое дерево, которое вы можете получить с помощью метода GetStructure() класса SyntaxTrivia.

person Kevin Pilch    schedule 30.12.2011
comment
Хорошо, но вопрос не в том, как я могу найти комментарии среди дочерних элементов, а в том, почему добавление нового комментария влияет (очевидно) на структуру синтаксического дерева. На самом деле, учитывая, что comments как, например, empty spaces, preprocessor dirrectives не являются частью структуры синтаксического дерева, почему их добавление/удаление должно каким-либо образом влиять каким-либо потомком родителяСинтаксический узел? - person Tigran; 31.12.2011
comment
Каждое изменение, которое вы вносите в файл, превращает его в другое дерево. В Roslyn объекты SyntaxTree и SyntaxNode являются неизменяемыми и полностью репрезентативными. После создания они никогда не меняются и моделируют каждый бит текста в файле. Это означает, что если вы вносите изменения в код, вы каждый раз будете получать другое дерево, потому что его ChildNodes будут иметь разные значения для своих Span свойств. - person Kevin Pilch; 31.12.2011
comment
Обратите внимание, что если вы редактируете в Visual Studio, мы используем стратегию поэтапного синтаксического анализа, так что мы фактически делим большую часть внутреннего хранилища дерева между синтаксическими анализами, это только тонкий слой объектов-оболочек, которые создаются при доступе, которые на самом деле являются новыми каждый раз. время. Технический документ хорошо объясняет процесс повторного раскручивания, который мы здесь используем. - person Kevin Pilch; 31.12.2011
comment
Но я перестраиваю его каждый раз, естественно. Когда я изменяю исходный файл, я останавливаюсь и перестраиваю свою программу, то есть, в моем понимании, я реконструирую SyntaxTree из нового файла. - person Tigran; 31.12.2011
comment
Как вы добавляете комментарий? Вы делаете это программно? Если это так, и вы используете что-то вроде SyntaxNode.Replace, вам нужно сохранить возвращаемое значение и просмотреть его дерево, чтобы увидеть измененные узлы. - person Kevin Pilch; 31.12.2011
comment
нет нет, Кевин, я просто 1. Остановить VS 2. Добавить комментарий вручную 3. Пересобрать программу. На самом деле это так просто, что я сам удивился, почему он меняет свое поведение. Или, как я уже сказал, я упускаю что-то действительно очевидное. - person Tigran; 31.12.2011
comment
давайте продолжим обсуждение в чате - person Kevin Pilch; 31.12.2011

В таких ситуациях использование CSharpSyntaxWalker может оказаться очень полезным.

Ниже приведен пример, извлеченный из блога Джоша Варти здесь, что помогает распечатать синтаксическое дерево на консоли:

 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());
}

Вы также можете распечатать викторины, вызвав методы GetLeadingTrivia и GetTrailingTrivia на синтаксических узлах.

person doer_uvc    schedule 15.12.2017