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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With