Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making several modifications to a SyntaxTree at once

Tags:

c#

.net

roslyn

I'm wanting to make several modifications to a Roslyn syntax tree at once, all around the same area of code

tree = tree.ReplaceNodes(oldNode, newNode).RemoveNode(toRemove);

however, only the first modification succeeds. It seems that the first change changes all the nodes around it, so the RemoveNodes method no longer finds toRemove in the resulting tree. I really, really, don't want to re-do the work to re-calculate toRemove in the new tree, and using a single SyntaxRewriter to perform all the work (overriding the DefaultVisit method) is ridiculously slow.

How can I do what I want?

like image 403
thecoop Avatar asked Nov 17 '12 21:11

thecoop


1 Answers

Before I offer a few alternatives, your comment that a SyntaxRewriter is "ridiculously slow" is a bit surprising. When you say "slow" do you mean "it's a lot of code to write" or "it's performing terribly"? That is the fastest (execution time wise) way to do multiple replacements, and both ReplaceNodes and RemoveNode use a rewriter internally. If you were having performance problems, make sure when you implement your DefaultVisit that you only visit child types if the nodes you're interested in are under the node it's called on. The simple trick is to compare spans and make sure the span of the node passed intersects with the nodes you are processing.

Anyways, SyntaxAnnotations provide a useful way to locate nodes in trees after a modification. You can just create an instance of the type, and attach it to a node with the WithAdditionalAnnotations extension method. You can locate the node again with the GetAnnotatedNodesOrTokens method.

So one way to approach your problem is to annotate your toRemove, and then when you call ReplaceNodes do two replacements in the same call -- one to do the oldNode -> newNode replacement and then one to do the toRemove -> toRemoveWithAnnotation replacement. Then find the annotated node in the resulting tree and call RemoveNode.

If you know that oldNode and toRemove aren't ancestors of each other (i.e. they're in unrelated parts of the tree), another option would be to reverse the ordering. Grab the parent node (call it oldNodeParent) of toRemove and call RemoveNode, meaning you get an updated parent node (call it oldNodeParentRewritten). Then, call ReplaceNodes doing two replacements: oldNode -> newNode and oldNodeParent -> oldNodeParentRewritten. No annotations needed.

like image 99
Jason Malinowski Avatar answered Oct 10 '22 01:10

Jason Malinowski