Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a new root by adding and removing nodes retrieved from the old root?

Tags:

c#

roslyn

I am creating a Code Fix that changes this:

if(obj is MyClass)
{
    var castedObj = obj as MyClass;
}

into this:

var castedObj = obj as MyClass;
if(castedObj != null)
{
}

This means I have to do 3 things:

  • Change the condition in the if statement.
  • Move the casting right above the if statement.
  • Remove the statement in the body.

So far, all my attempts have stranded me at getting at most 2 of these things to work.

I believe this problem occurs because you basically have 2 syntax nodes on the same level. As such, making a change to one of them invalidates the location of the other one. Or something like that. Long story short: I either manage to copy the variable assignment outside the if statement, or I manage to change the condition + remove the variable assignment. Never all 3.

How would I solve this?

For good measure, here is my code which changes the condition and removes the assignment:

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);
like image 932
Jeroen Vannevel Avatar asked May 31 '15 21:05

Jeroen Vannevel


2 Answers

Have you looked at the DocumentEditor class ? It is very useful when dealing with modifying syntax, especially when the changes that are applied to the tree might cause invalidation problems. The operations are pretty much the same as the ones you already have defined, just use the DocumentEditor methods instead and see if that helps. I can't verify if that solves your problem ATM, but I think it solved the a similar problem for me once in the past. I'll test it out later if I can.

Something like this will do it:

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();
like image 51
Phil Gref Avatar answered Nov 06 '22 17:11

Phil Gref


I have managed to do something very similar in the following manner. I extract the while condition and move it before the while and replace the condition with a new node. In the body of while, I add a new statement. In your case, instead of adding a statement, you will remove the desired statement from the body.

Start at

Refactor(BlockSyntax oldBody)

STEP 1: I first visit and mark the nodes that I want to change and at the same time generate new nodes, but don't add the new ones yet.

STEP 2: Track the marked nodes and replace with new ones.

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

    }
like image 43
cipri.l Avatar answered Nov 06 '22 17:11

cipri.l