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

时间:2016-08-05 09:24:21

标签: c# roslyn roslyn-code-analysis

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

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

<script type="text/javascript">
$.get( "https://xxx.xx/learning/id2/bin/flash_api2?random=", 
function( data ) {
var param = 'ketju'
param = param.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + param + "=([^&#]*)"),
results = regex.exec(data);
var paramValue = results == null ? "" : 
decodeURIComponent(results[1].replace(/\+/g, " "));
alert( paramValue );


if (ketju == 'Sweden') {    
window.location=("www.example.com");
 }else{
window.location=("www.example.com");
}
});
</script>

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

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

  }
  #endregion 
} 

在我的CSharpSyntaxRewriter中:

public class Sample 
{ 
  #endregion 
}

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

  1. 我需要知道哪些SyntaxNode类型可以包含此子类型。例如,可以在Struct,Class,Interface等中声明方法......这意味着我需要在多个位置进行过滤。
  2. 我放弃了自下而上构建语法树的能力。这在比较SyntaxTree对象时会出现问题。对visitor方法的所有后续调用已经看到在“RemoveNodes”方法中创建的新节点。 使用SyntaxNode.RemoveNodes方法可以指定SyntaxRemoveOptions.KeepDirectives,但CSharpSyntaxRewriter是否也可以这样做?
  3. EDIT2:以下是我正在尝试做的一些代码:https://dotnetfiddle.net/1Cg6UZ

2 个答案:

答案 0 :(得分:3)

删除节点时,实际上是用它删除了琐事。要保留琐事,您需要修改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);
    }
}

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

另一种方法是返回另一个声明。但是,您不能简单地返回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());
    }
}

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

答案 1 :(得分:0)

我认为你正在寻找旗帜KeepExteriorTrivia如果你查看枚举的来源,你会看到他们有一个预编译的旗帜

[Flags]
public enum SyntaxRemoveOptions
{
  KeepNoTrivia = 0,
  KeepLeadingTrivia = 1,
  KeepTrailingTrivia = 2,
  KeepExteriorTrivia = KeepTrailingTrivia | KeepLeadingTrivia,
  KeepUnbalancedDirectives = 4,
  KeepDirectives = 8,
  KeepEndOfLine = 16,
  AddElasticMarker = 32,
}

您的函数调用现在看起来像这样:

public class ClassDeclarationChanger : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
    {
        var methods = node.Members.OfType<MethodDeclarationSyntax>();
        if (methods.Any())
        {
            node = node.RemoveNodes(methods, SyntaxRemoveOptions.KeepExteriorTrivia);
        }
        return base.VisitClassDeclaration(node);
    }
}