删除节点时保持结构化的琐事

TWT*_*TWT 5 c# roslyn roslyn-code-analysis

有没有一种简单的方法可以从树中删除 SyntaxNode(即方法),但保留结构化的琐事?

在下面的代码中,我想删除 MethodA:

public class Sample 
{ 
  #region SomeRegion 
  public void MethodA() 
  { 

  }
  #endregion 
} 
Run Code Online (Sandbox Code Playgroud)

我使用 CSharpSyntaxRewriter 来重写 SyntaxTree。在 VisitMethodDeclaration 方法中,我只是为 MethodA 返回 null。这种方法的问题在于 #region 标签的 StructuredTrivia 也被删除了。这是结果结果:

public class Sample 
{ 
  #endregion 
}
Run Code Online (Sandbox Code Playgroud)

在我的 CSharpSyntaxRewriter 中:

public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) 
{ 
   if (...) 
      return null; 
   else 
      return node; 
} 
Run Code Online (Sandbox Code Playgroud)

编辑:如以下答案之一所述,我可以将 SyntaxNode.RemoveNodes 与 SyntaxRemoveOptions.KeepDirectives 选项一起使用。这个解决方案有两个很大的缺点:

  1. 我需要知道哪些 SyntaxNode 类型可以包含这个子类型。例如,可以在结构、类、接口等中声明方法……这意味着我需要在多个位置进行过滤。
  2. 我失去了自下而上构建语法树的能力。这在比较 SyntaxTree 对象时会出现问题。对访问者方法的所有后续调用都已经看到在“RemoveNodes”方法中创建的新节点。使用 SyntaxNode.RemoveNodes 方法可以指定 SyntaxRemoveOptions.KeepDirectives,但这也可以使用 CSharpSyntaxRewriter 吗?

EDIT2:这是我正在尝试做的一些代码:https : //dotnetfiddle.net/1Cg6UZ

SJP*_*SJP 5

删除节点时,您实际上是在删除与它相关的琐事。要保留细节,您需要修改 ClassDeclarationSyntax 而不是 MethodDeclaration。

访问 ClassDeclarationSyntax 时,您可以通过删除适当的节点并使用 SyntaxRemoveOptions.KeepTrailingTrivia | 来修改类。SyntaxRemoveOptions.KeepLeadingTrivia 您可以保留实际方法定义之前和之后的注释和区域语句。

public class ClassDeclarationChanger : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
    {
        var methods = node.Members.OfType<MethodDeclarationSyntax>();
        if (methods.Any())
        {
            node = node.RemoveNodes(methods, SyntaxRemoveOptions.KeepTrailingTrivia | 
                    SyntaxRemoveOptions.KeepLeadingTrivia);
        }
        return base.VisitClassDeclaration(node);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您希望首先访问子节点,您当然也可以先执行 base.VisitClassDeclaration(node) ,然后仅删除方法节点。

另一种方法是返回另一个声明。但是,您不能简单地返回 EmptyStatement(因为这将导致异常),但您可以插入一个不带内容的新方法声明:

public class SampleChanger : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        // Generates a node containing only parenthesis 
        // with no identifier, no return type and no parameters
        var newNode = SyntaxFactory.MethodDeclaration(SyntaxFactory.IdentifierName(""), "");
        // Removes the parenthesis from the Parameter List 
        // and replaces them with MissingTokens
        newNode = newNode.ReplaceNode(newNode.ParameterList,
            newNode.ParameterList.WithOpenParenToken(
                SyntaxFactory.MissingToken(SyntaxKind.OpenParenToken)).
            WithCloseParenToken(SyntaxFactory.MissingToken(SyntaxKind.CloseParenToken)));
        // Returns the new method containing no content 
        // but the Leading and Trailing trivia of the previous node
        return newNode.WithLeadingTrivia(node.GetLeadingTrivia()).
            WithTrailingTrivia(node.GetTrailingTrivia());
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这种方法确实有一个缺点,即它需要针对不同语法类型的特定 SyntaxNode,因此将其重用于代码的其他部分可能会很困难。