Используя Roslyn, как перечислить детали элементов (пространства имен, классов и т. д.) в документе Visual Basic?

При использовании Roslyn единственным механизмом определения элементов документа Visual Basic является:

var members = SyntaxTree.GetRoot().DescendantNodes().Where(node =>
                          node is ClassStatementSyntax ||
                          node is FunctionAggregationSyntax ||
                          node is IncompleteMemberSyntax ||
                          node is MethodBaseSyntax ||
                          node is ModuleStatementSyntax ||
                          node is NamespaceStatementSyntax ||
                          node is PropertyStatementSyntax ||
                          node is SubNewStatementSyntax
                        );

Как получить имя участника, StarLineNumber и EndLineNumber каждого участника?


person Joginder S Nahil    schedule 01.05.2018    source источник
comment
Добро пожаловать в СО. Вы можете использовать обратные кавычки, чтобы встроенная метка работала как код   -  person Neuron    schedule 04.06.2018


Ответы (1)


Существует не только один способ его получения:

1) Как попробуете: я не буду показывать этот способ для всех членов рода (их количество огромное и логика схожая), а только один из них, например ClassStatementSyntax:

  • чтобы получить это имя, просто получите ClassStatementSyntax.Identifier.ValueText
  • чтобы получить стартовую линию, вы можете использовать Location как один из способов:
var location = Location.Create(SyntaxTree, ClassStatementSyntax.Identifier.Span);
var startLine = location.GetLineSpan().StartLinePosition.Line;
  • логика получения конечной строки выглядит как логика получения начальной строки, но она зависит от соответствующего закрывающего оператора (какого-то конечного оператора или самого себя)

2) Более полезный способ — использовать SemanticModel для получения нужных данных: Таким образом, вам нужно будет получить семантическую информацию только для ClassStatementSyntax, ModuleStatementSyntxt и NamespaceStatementSyntax, а все их члены будут получены просто вызовом GetMembers():

...

  SemanticModel semanticModel = // usually it is received from the corresponding compilation
  var typeSyntax = // ClassStatementSyntax, ModuleStatementSyntxt or NamespaceStatementSyntax
  string name = null;
  int startLine;
  int endLine;

  var info = semanticModel.GetSymbolInfo(typeSyntax);
  if (info.Symbol is INamespaceOrTypeSymbol typeSymbol)
  {
      name = typeSymbol.Name; // retrieve Name
      startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
      endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line
      foreach (var item in typeSymbol.GetMembers())
      {
          // do the same logic for retrieving name and lines for all others members without calling GetMembers()
      }
  }
  else if (semanticModel.GetDeclaredSymbol(typeSyntax) is INamespaceOrTypeSymbol typeSymbol2)
  {
      name = typeSymbol2.Name; // retrieve Name
      startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
      endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line

      foreach (var item in typeSymbol2.GetMembers())
      {
          // do the same logic for retrieving name and lines for all others members without calling GetMembers()
      }
  }

Но внимание, когда у вас есть частичное объявление, ваш DeclaringSyntaxReferences будет иметь пару элементов, поэтому вам нужно отфильтровать SyntaxReference по вашему текущему SyntaxTree

person George Alexandria    schedule 12.06.2018
comment
Большое спасибо Джордж. На следующий день или около того я реализую ваше решение. - person Joginder S Nahil; 14.06.2018
comment
Любая идея, почему startLine и endLine имеют одинаковое значение? - person Joginder S Nahil; 14.06.2018
comment
@JoginderSNahil, это означает, что у вас есть какое-то объявление, которое может полностью располагаться в одной строке, например, FieldDeclarationSyntax,PropertyDeclarationSyntax (без Get, Set), MethodStatementSyntax (для метода MustOverride) могут располагаться в одной строке и т. д., поэтому startLine и endLine будут одинаковыми, но startPosition и endPosition, конечно, будут разными. - person George Alexandria; 14.06.2018
comment
Я проанализировал только объявления пространств имен и классов, и они не находятся в одной строке. Логика у вас работает? - person Joginder S Nahil; 17.06.2018
comment
@JoginderSNahil, приношу свои извинения, я действительно считал, что ISymbol.Locations содержит полный диапазон элемента, а не только начало объявления. Я обновил ответ, изменил использование Locations в DeclaringSyntaxReferences - person George Alexandria; 17.06.2018
comment
Ваши изменения работают отлично. Большое спасибо за такой подробный ответ. - person Joginder S Nahil; 18.06.2018
comment
@JoginderSNahil, вы по ошибке проголосовали за ответ или кто-то другой? :) - person George Alexandria; 18.06.2018
comment
Я не понял, что я сделал это! Как я могу сказать это правильно? - person Joginder S Nahil; 19.06.2018
comment
@JoginderSNahil, попробуйте проголосовать, если можете (хотите), иначе я не знаю, как отменить это, и мы не беспокоимся об этом :) - person George Alexandria; 19.06.2018