Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Insert new expression after statement via roslyn

Tags:

c#

roslyn

I have some problems with adding an expression to the SyntaxTree with Roslyn. What I need to achieve is the following: Whenever I find a special statement, I want to insert one or more expressions after this statement.

Let's say I want to insert the statement "myVar = myVar + 1" after every statement which writes the variable "testVar".

So the following snippet:

 a = 10;
 testVar = 50;
 a = testVar / a;
 testVar = a;

Should be turned into this piece of code:

 a = 10;
 testVar = 50;
 myVar = myVar + 1;
 a = testVar / a;
 testVar = a;
 myVar = myVar + 1;

My current approach uses the SyntaxVisitor with the method 'SyntaxNode VisitExpressionStatement(ExpressionStatement node)'. This method visits all expressions in the SyntaxTree and allows to replace the visited expression with the SyntaxNode it returns. However, I do not want to replace statements, but add new expressions after them which basically requires two expressions to be returned. The only solution I found to this is using "BlockSyntax" which serves as a container for the two expressions (see code snippet [0]). Unfortunately, "BlockSyntax" introduces curly braces around itself which lead to the following result:

 a = 10;
 {
     testVar = 50;
     myVar = myVar + 1;
 }
 a = testVar / a;
 {
     testVar = a;
     myVar = myVar + 1;
 }

This approach is unacceptable for me, as I don't want to manipulate the scopes. Is there any way to insert arbitrary expressions at a location of my choice with Roslyn?

[0]

public SyntaxNode VisitExpressionStatement(ExpressionStatement node){
    if(node has special characteristics){
        var newExpression = ...

        var newStatmentList = new Roslyn.Compilers.CSharp.SyntaxList<StatementSyntax>();
        newStatmentList = newStatmentList.Insert(newStatmentList.Count, node);
        newStatmentList = newStatmentList.Insert(newStatmentList.Count, newExpression);

        BlockSyntax newBlock = Syntax.Block(newStatmentList);
        return newBlock;

    }
    else {
        return node;
    }
}
like image 299
Triffi Avatar asked Nov 10 '22 14:11

Triffi


1 Answers

My strategy has been to cheat with BlockSyntax. See my similar question.

So I add BlockSyntax as you've done, but then I "remove" them by marking the { and } tokens as missing. I haven't run into any issues with this approach yet, but it seems like more of a workaround than a solution.

var statements = new SyntaxList<StatementSyntax>();
//Tried bundling newNode and invocation together
statements.Add(SyntaxFactory.ExpressionStatement(newNode));
statements.Add(SyntaxFactory.ExpressionStatement(invocation));
var wrapper = SyntaxFactory.Block(statements);

//Now we can remove the { and } braces
wrapper = wrapper.WithOpenBraceToken(SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken))
.WithCloseBraceToken(SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken));

Note that I say I'm "removing" them. The issue here is that the SyntaxTree you'll generate will still appear to the C# compiler as though it has a BlockSyntax at the point you've placed it. This may or may not matter.

For example:

  • If you're outputting this tree as a string to a file, you'll be fine.

  • If you're immediately compiling this tree, I believe the compiler will interpret the BlockSyntax as existing where you've rewritten it and all scoping semantics will still be enforced as if it was there.

For more on generating "weird" trees check out my blog post: Don't Trust SyntaxNode.ToFullString()

like image 56
JoshVarty Avatar answered Nov 14 '22 23:11

JoshVarty