Почему SyntaxNode.ReplaceNode изменяет параметры SyntaxTree?

Я пытаюсь заменить узлы в дереве синтаксиса в Roslyn, и он просто работает, но с досадой, которая считает, что это не должно быть проблемой.

Дерево синтаксиса создается из script, и я хочу, чтобы результат был также деревом синтаксиса на основе script, но по какой-то причине замена node в дереве создает новое дерево синтаксиса с измененные параметры: Kind становится Regular вместо Script. Это исправление с помощью SyntaxTree.WithRootAndOptions, но мне кажется, что я делаю что-то неправильно, если мне нужно это назвать.

Пример программы:

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
    }
}

(Вывод в комментариях.)

Является ли это обходным путем действительно правильным, или есть какой-то другой способ, который я должен заменить node в дереве?

Ответы

Ответ 1

При замене узлов в дереве вы создаете новое поддерево узлов. По сути, это новое поддерево не содержится в SyntaxTree. Однако свойство SyntaxTree на node вызывает новый, если вы его заметили. В то время, когда он делает это, оригинальный SyntaxTree давно ушел, поэтому его невозможно сохранить параметры синтаксического анализа. Даже если бы это было возможно, сохранение опций было бы бессмысленным, потому что у вас больше не было дерева, созданного парсером.

Причина, по которой Roslyn создает этот SyntaxTree, заключается в том, что все подэлементы технически содержатся в экземпляре SyntaxTree, так что Roslyn может связать с ним диагностику. Это полезно, если вы используете API-интерфейс SemanticModel для попытки связывания и получения семантической информации для фрагментов деревьев, которые в настоящее время не являются частью компиляции. Диагностика сообщает об ошибке и ее местоположении, которая обозначает экземпляр дерева, в котором он находится.