Почему 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 для попытки связывания и получения семантической информации для фрагментов деревьев, которые в настоящее время не являются частью компиляции. Диагностика сообщает об ошибке и ее местоположении, которая обозначает экземпляр дерева, в котором он находится.