-->

Using Roslyn, how to enumerate members (Namespaces

2020-05-07 06:18发布

问题:

Using Roslyn, the only mechanism for determining members of Visual Basic document appears to be:

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

How do get the member name, StarLineNumber and EndLineNumber of each member?

回答1:

Exists not only the one way to get it:

1) As you try: I willn't show this way for all of kind member (they count are huge and the logic is the similar), but only a one of them, for example ClassStatementSyntax:

  • to achive it name just get ClassStatementSyntax.Identifier.ValueText
  • to get start line you can use Location as one of ways:
var location = Location.Create(SyntaxTree, ClassStatementSyntax.Identifier.Span);
var startLine = location.GetLineSpan().StartLinePosition.Line;
  • logic for retrieving the end line looks like a logic to receive the start line but it dependents on the corresponding closing statement (some kind of end statement or self)

2) More useful way – use SemanticModel to get a data that you want: In this way you will need to receive semantic info only for ClassStatementSyntax, ModuleStatementSyntxt and NamespaceStatementSyntax, and all of their members will be received just calling 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()
      }
  }

But attention, when you have a partial declaration your DeclaringSyntaxReferences will have a couple items, so you need to filter SyntaxReference by your current SyntaxTree