为什么 SyntaxNode.ReplaceNode 会更改 SyntaxTree 选项? [英] Why does SyntaxNode.ReplaceNode change the SyntaxTree options?

查看:38
本文介绍了为什么 SyntaxNode.ReplaceNode 会更改 SyntaxTree 选项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Roslyn 中替换语法树中的节点,这只是关于工作,但有一种烦恼,感觉它不应该成为问题.

I'm trying to replace nodes within a syntax tree in Roslyn, and it's just about working, but with an annoyance which feels it shouldn't be a problem.

语法树是从脚本生成的,我也希望结果是基于脚本的语法树 - 但出于某种原因,替换树中的节点会创建一个带有更改选项的新语法树:Kind 变成 Regular 而不是 Script.这可以通过 SyntaxTree.WithRootAndOptions 解决,但如果我需要调用它,感觉就像我做错了什么.

The syntax tree is generated from a script, and I want the result to be a script-based syntax tree too - but for some reason, replacing a node in the tree creates a new syntax tree with changed options: the Kind becomes Regular instead of Script. That's fixable with SyntaxTree.WithRootAndOptions but it feels like I'm doing something wrong if I need to call that.

示例程序:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Scripting;
using System;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        Script script = CSharpScript.Create("Console.WriteLine(\"Before\")",
            ScriptOptions.Default.AddImports("System"));

        var compilation = script.GetCompilation();
        var tree = compilation.SyntaxTrees.Single();

        var after = SyntaxFactory.LiteralExpression(
            SyntaxKind.StringLiteralExpression,
            SyntaxFactory.Literal("After"));

        var root = tree.GetRoot();
        var before = root.DescendantNodes().OfType<LiteralExpressionSyntax>().Single();
        var newRoot = root.ReplaceNode(before, after);
        var fixedTree = newRoot.SyntaxTree.WithRootAndOptions(newRoot, tree.Options);

        Console.WriteLine(newRoot);                         // Console.WriteLine("After")
        Console.WriteLine(tree.Options.Kind);               // Script
        Console.WriteLine(newRoot.SyntaxTree.Options.Kind); // Regular
        Console.WriteLine(fixedTree.Options.Kind);          // Script
    }
}

(输出在评论中.)

这种解决方法是否真的正确,或者我应该用什么不同的方式来替换树中的节点?

Is this workaround actually correct, or is there some different way I should be replacing the node in the tree?

推荐答案

当您替换树中的节点时,您将创建一个新的节点子树.本质上,这个新的子树不包含在 SyntaxTree 中.但是,如果您观察过该节点上的 SyntaxTree 属性,则会召唤出一个新属性.在执行此操作时,原始 SyntaxTree 早已不复存在,因此无法保留解析选项.即使可能,保留选项也毫无意义,因为您不再拥有解析器生成的树.

When you replace nodes in a tree, you create a new sub-tree of nodes. Essentially, this new sub-tree is not contained within a SyntaxTree. However, the SyntaxTree property on the node conjurs up a new one if you ever observe it. At the time it does this, the original SyntaxTree is long gone, so its not possible to retain the parse options. Even if it were possible, retaining the options would be meaningless because you no longer have a tree produced by the parser.

Roslyn 创建此 SyntaxTree 的原因是,所有子树在技术上都包含在 SyntaxTree 实例中,以便 Roslyn 可以将诊断与其关联.如果您使用 SemanticModel 的探索性 API 来尝试绑定 &获取当前不属于编译的树片段的语义信息.诊断报告错误及其位置,表示错误所在的树实例.

The reason Roslyn creates this SyntaxTree is that so all sub-trees are technically contained within a SyntaxTree instance, so that Roslyn can associated diagnostics with it. This is useful if you use the SemanticModel's exploratory API's to attempt to bind & get semantic info for fragments of trees that are not currently part of the compilation. The diagnostic reports the error and its location, which denotes the tree instance it is within.

这篇关于为什么 SyntaxNode.ReplaceNode 会更改 SyntaxTree 选项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆