Как создать новый корень, добавив и удалив узлы, извлеченные из старого корня?
Я создаю Code Fix, который меняет это:
if(obj is MyClass)
{
var castedObj = obj as MyClass;
}
в это:
var castedObj = obj as MyClass;
if(castedObj != null)
{
}
Это означает, что я должен сделать 3 вещи:
- Измените условие в инструкции
if
.
- Переместите кастинг прямо над оператором
if
.
- Удалить оператор в теле.
До сих пор все мои попытки застряли в том, что я получаю не более двух таких вещей.
Я считаю, что эта проблема возникает, потому что у вас в основном есть 2 синтаксических узла на одном уровне. Таким образом, изменение одного из них делает недействительным местоположение другого. Или что-то типа того. Короче говоря: я либо копирую назначение переменной вне оператора if
, либо мне удается изменить условие + удалить назначение переменной. Никогда не 3.
Как я могу это решить?
Для хорошей меры, вот мой код, который изменяет условие и удаляет назначение:
var newIfStatement = ifStatement.RemoveNode(
variableDeclaration,
SyntaxRemoveOptions.KeepExteriorTrivia);
newIfStatement = newIfStatement.ReplaceNode(newIfStatement.Condition, newCondition);
var ifParent = ifStatement.Parent;
var newParent = ifParent.ReplaceNode(ifStatement, newIfStatement);
newParent = newParent.InsertNodesBefore(
newIfStatement,
new[] { variableDeclaration })
.WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(ifParent, newParent);
Ответы
Ответ 1
Вы просмотрели класс DocumentEditor
? Это очень полезно при работе с модификацией синтаксиса, особенно когда изменения, которые применяются к дереву, могут вызвать проблемы с недействительностью. Операции почти такие же, как те, которые вы уже определили, просто используйте методы DocumentEditor
и посмотрите, поможет ли это. Я не могу проверить, решает ли это ваш проблемный банкомат, но я думаю, что он решил подобную проблему для меня однажды в прошлом. Я проверю его позже, если смогу.
Что-то вроде этого сделает это:
var editor = await DocumentEditor.CreateAsync(document);
editor.RemoveNode(variableDeclaration);
editor.ReplaceNode(ifStatement.Condition, newCondition);
editor.InsertBefore(ifStatement,
new[] { variableDeclaration.WithAdditionalAnnotations(Formatter.Annotation) });
var newDocument = editor.GetChangedDocument();
Ответ 2
Мне удалось сделать что-то очень похожее следующим образом.
Я извлекаю условие while и перемещаю его до времени и заменяю условие новым node.
В то же время я добавляю новое утверждение.
В вашем случае вместо добавления инструкции вы удалите требуемое утверждение из тела.
Начните с
Refactor(BlockSyntax oldBody)
ШАГ 1: я сначала посещаю и отмечаю узлы, которые я хочу изменить, и в то же время генерирую новые узлы, но пока не добавляю новые.
ШАГ 2: Отслеживайте отмеченные узлы и заменяйте их новыми.
class WhileConditionRefactoringVisitor : CSharpSyntaxRewriter
{
private static int CONDITION_COUNTER = 0;
private static string CONDITION_VAR = "whileCondition_";
private static string ConditionIdentifier
{
get { return CONDITION_VAR + CONDITION_COUNTER++; }
}
private readonly List<SyntaxNode> markedNodes = new List<SyntaxNode>();
private readonly List<Tuple<ExpressionSyntax, IdentifierNameSyntax, StatementSyntax, WhileStatementSyntax>> replacementNodes =
new List<Tuple<ExpressionSyntax, IdentifierNameSyntax, StatementSyntax, WhileStatementSyntax>>();
//STEP 1
public override SyntaxNode VisitWhileStatement(WhileStatementSyntax node)
{
var nodeVisited = (WhileStatementSyntax) base.VisitWhileStatement(node);
var condition = nodeVisited.Condition;
if (condition.Kind() == SyntaxKind.IdentifierName)
return nodeVisited;
string conditionVarIdentifier = ConditionIdentifier;
var newConditionVar = SyntaxFactoryExtensions.GenerateLocalVariableDeclaration(conditionVarIdentifier,
condition, SyntaxKind.BoolKeyword).NormalizeWhitespace().WithTriviaFrom(nodeVisited);
var newCondition = SyntaxFactory.IdentifierName(conditionVarIdentifier).WithTriviaFrom(condition);
markedNodes.Add(condition);
markedNodes.Add(node);
replacementNodes.Add(new Tuple<ExpressionSyntax, IdentifierNameSyntax, StatementSyntax, WhileStatementSyntax>(condition, newCondition, newConditionVar, node));
return nodeVisited;
}
//STEP 2
private BlockSyntax ReplaceNodes(BlockSyntax oldBody)
{
oldBody = oldBody.TrackNodes(this.markedNodes);
foreach (var tuple in this.replacementNodes)
{
var currentA = oldBody.GetCurrentNode(tuple.Item1);
if (currentA != null)
{
var whileStatement = currentA.Parent;
oldBody = oldBody.InsertNodesBefore(whileStatement, new List<SyntaxNode>() { tuple.Item3 });
var currentB = oldBody.GetCurrentNode(tuple.Item1);
oldBody = oldBody.ReplaceNode(currentB, tuple.Item2);
var currentWhile = oldBody.GetCurrentNode(tuple.Item4);
//modify body
var whileBody = currentWhile.Statement as BlockSyntax;
//create new statement
var localCondition = tuple.Item3 as LocalDeclarationStatementSyntax;
var initializer = localCondition.Declaration.Variables.First();
var assignment = SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
SyntaxFactory.IdentifierName(initializer.Identifier), initializer.Initializer.Value));
var newStatements = whileBody.Statements.Add(assignment);
whileBody = whileBody.WithStatements(newStatements);
//updateWhile
var newWhile = currentWhile.WithStatement(whileBody);
oldBody = oldBody.ReplaceNode(currentWhile, newWhile);
}
}
return oldBody;
}
public BlockSyntax Refactor(BlockSyntax oldBody)
{
markedNodes.Clear();
replacementNodes.Clear();
//STEP 1
oldBody = (BlockSyntax)this.Visit(oldBody);
//STEP 2
oldBody = this.ReplaceNodes(oldBody);
return oldBody;
}
}