Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 替换Roslyn语法树中的嵌套节点_C#_Roslyn_Abstract Syntax Tree - Fatal编程技术网

C# 替换Roslyn语法树中的嵌套节点

C# 替换Roslyn语法树中的嵌套节点,c#,roslyn,abstract-syntax-tree,C#,Roslyn,Abstract Syntax Tree,作为自定义编译过程的一部分,我将替换SyntaxTree中的各种节点,以生成有效的C。当替换的节点嵌套时,就会出现问题,因为所有类型的不变性意味着一旦替换出一个节点,它的层次结构中就不再平等 然而,它似乎已经瞄准了旧版本的Roslyn,并且依赖于一些现在是私有的方法。我已经有了一个SyntaxTree和一个SemanticModel,但到目前为止我还不需要文档、项目或解决方案,所以我一直在犹豫是否要走这条路 假设我有以下字符串public void Test{coshx;},我想将其转换为pub

作为自定义编译过程的一部分,我将替换SyntaxTree中的各种节点,以生成有效的C。当替换的节点嵌套时,就会出现问题,因为所有类型的不变性意味着一旦替换出一个节点,它的层次结构中就不再平等

然而,它似乎已经瞄准了旧版本的Roslyn,并且依赖于一些现在是私有的方法。我已经有了一个SyntaxTree和一个SemanticModel,但到目前为止我还不需要文档、项目或解决方案,所以我一直在犹豫是否要走这条路

假设我有以下字符串public void Test{coshx;},我想将其转换为public void Test{MathNet.Numerics.Trig.Cosh_u解析器[x];}

我第一次尝试使用ReplaceNodes失败,因为一旦进行了一次替换,树就会发生足够的变化,导致第二次比较失败。因此,仅进行cosh替换,x保持不变:

public static void TestSyntaxReplace()
{
  const string code = "public void Test() { cosh(x); }";
  var tree = CSharpSyntaxTree.ParseText(code);
  var root = tree.GetRoot();
  var swap = new Dictionary<SyntaxNode, SyntaxNode>();

  foreach (var node in root.DescendantNodes())
    if (node is InvocationExpressionSyntax oldInvocation)
    {
      var newExpression = ParseExpression("MathNet.Numerics.Trig.Cosh");
      var newInvocation = InvocationExpression(newExpression, oldInvocation.ArgumentList);
      swap.Add(node, newInvocation);
    }

  foreach (var node in root.DescendantNodes())
    if (node is IdentifierNameSyntax identifier)
      if (identifier.ToString() == "x")
      {
        var resolver = IdentifierName("__resolver");
        var literal = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(identifier.ToString()));
        var argument = BracketedArgumentList(SingletonSeparatedList(Argument(literal)));
        var resolverCall = ElementAccessExpression(resolver, argument);
        swap.Add(node, resolverCall);
      }

  root = root.ReplaceNodes(swap.Keys, (n1, n2) => swap[n1]);
  var newCode = root.ToString();
}
问题:CSharpSyntaxVisitor是正确的方法吗?如果是这样的话,我们如何让它工作呢

答案正如George Alexandria所提供的,首先调用基本访问方法是至关重要的,否则SemanticModel将无法再使用。这是适用于我的SyntaxRewriter:

private sealed class NonCsNodeRewriter : CSharpSyntaxRewriter
{
  private readonly SemanticModel _model;
  public NonCsNodeRewriter(SemanticModel model)
  {
    _model = model;
  }

  public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
  {
    var invocation = (InvocationExpressionSyntax)base.VisitInvocationExpression(node);
    var symbol = _model.GetSymbolInfo(node);
    if (symbol.Symbol == null)
      if (!symbol.CandidateSymbols.Any())
      {
        var methodName = node.Expression.ToString();
        if (_methodMap.TryGetValue(methodName, out var mapped))
          return InvocationExpression(mapped, invocation.ArgumentList);
      }

    return invocation;
  }
  public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
  {
    var identifier = base.VisitIdentifierName(node);
    var symbol = _model.GetSymbolInfo(node);
    if (symbol.Symbol == null)
      if (!symbol.CandidateSymbols.Any())
      {
        // Do not replace unknown methods, only unknown variables.
        if (node.Parent.IsKind(SyntaxKind.InvocationExpression))
          return identifier;

        return CreateResolverIndexer(node.Identifier);
      }
    return identifier;
  }

  private static SyntaxNode CreateResolverIndexer(SyntaxToken token)
  {
    var literal = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(token.ToString()));
    var argument = BracketedArgumentList(SingletonSeparatedList(Argument(literal)));
    var indexer = ElementAccessExpression(IdentifierName("__resolver"), argument);
    return indexer;
  }
}
ReplaceNode是您需要的,但是您应该从深度中替换节点,以便在当前深度级别中,只有一个节点需要更改以进行比较

您可以重写第一个示例,保存交换顺序并保存一个中间SyntaxTree,这样就可以了。但是Roslyn内置了深度一阶重写的实现——CSharpSyntaxRewriter,在您发布的@JoshVarty指向CSharpSyntaxRewriter的链接中


您的第二个示例不起作用,因为您使用了定制的CSharpSyntaxVisitor,它在调用replacer.visitrot时不会深入到deep;您仅调用VisitComplationUnit。。。没有别的了。相反,CSharpSyntaxRewriter将转到子节点,并为所有子节点调用Visit*方法。

ReplaceNode是您需要的,但您应该从深度替换节点,以便在当前深度级别中,只有一个用于比较的更改。为了达到更深层次的一阶重写,Roslyn在上面的链接中有一个链接,您在@JoshVarty上发布的链接指向CSharpSyntaxRewriter。您还可以重写第一个示例,保存交换顺序并保存一个中间语法树,它会起作用。@GeorgeAlexandria我的第二次尝试使用CSharpSyntaxRewriter,但它似乎根本不起作用。我将尝试修改ReplaceNodes方法,以迭代所有交换对,并首先执行嵌套最深的交换对。谢谢你的想法。不,你第二次尝试使用定制的CSharpSyntaxVisitor,它不会通过设计CSharpSyntaxVisitor而深入。因此,当您调用var newRoot=replace.visitrot时;您真正调用的是VisitComplationIntrootSyntax,而不是其他任何东西。CSharpSyntaxRewriter将代替CSharpSyntaxRewriter转到子节点。@GeorgeAlexandria啊!重写者与访问者。就这样,谢谢!如果你把它作为答案贴出来,我可以接受。
private sealed class NonCsNodeRewriter : CSharpSyntaxRewriter
{
  private readonly SemanticModel _model;
  public NonCsNodeRewriter(SemanticModel model)
  {
    _model = model;
  }

  public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
  {
    var invocation = (InvocationExpressionSyntax)base.VisitInvocationExpression(node);
    var symbol = _model.GetSymbolInfo(node);
    if (symbol.Symbol == null)
      if (!symbol.CandidateSymbols.Any())
      {
        var methodName = node.Expression.ToString();
        if (_methodMap.TryGetValue(methodName, out var mapped))
          return InvocationExpression(mapped, invocation.ArgumentList);
      }

    return invocation;
  }
  public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
  {
    var identifier = base.VisitIdentifierName(node);
    var symbol = _model.GetSymbolInfo(node);
    if (symbol.Symbol == null)
      if (!symbol.CandidateSymbols.Any())
      {
        // Do not replace unknown methods, only unknown variables.
        if (node.Parent.IsKind(SyntaxKind.InvocationExpression))
          return identifier;

        return CreateResolverIndexer(node.Identifier);
      }
    return identifier;
  }

  private static SyntaxNode CreateResolverIndexer(SyntaxToken token)
  {
    var literal = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(token.ToString()));
    var argument = BracketedArgumentList(SingletonSeparatedList(Argument(literal)));
    var indexer = ElementAccessExpression(IdentifierName("__resolver"), argument);
    return indexer;
  }
}