Attila Hajdrik
Attila Hajdrik

Reputation: 212

String escaping issue with Roslyn statement creation

I've the following code, which builds up a Roslyn statement which calls Roslyn code inside, but I've a problem with string escaping.

Here is the code:

        var parseStatementArgument = "var statement = Syntax.ParseStatement(\\\"Console.WriteLine (\\\"Hello {0}\\\", parameter1);\\\");";

        var st = Syntax.InvocationExpression(
                                    Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
                                        .AddArgumentListArguments(
                                            Syntax.Argument(Syntax.LiteralExpression(
                                                SyntaxKind.StringLiteralExpression,
                                                Syntax.Literal(
                                                    text: "\"" + parseStatementArgument + "\"",
                                                    value: parseStatementArgument)
                                    )));

        var variableDeclarator = Syntax.VariableDeclarator(Syntax.Identifier("statement"))
            .WithInitializer(Syntax.EqualsValueClause(st));

        var varStatement = Syntax.VariableDeclaration(Syntax.IdentifierName("var"), Syntax.SeparatedList(variableDeclarator));

        var varStatementText = varStatement.Format().GetFormattedRoot().GetFullText() + ";";

        var scriptEngine = new ScriptEngine(
            new [] {
                MetadataReference.Create("Roslyn.Compilers"),
                MetadataReference.Create("Roslyn.Compilers.CSharp"),
                MetadataReference.Create("Roslyn.Services"),
                MetadataReference.Create("Roslyn.Services.CSharp")
            },
            new [] {
                "System",
                "Roslyn.Compilers.CSharp",
                "Roslyn.Scripting",
                "Roslyn.Scripting.CSharp",
                "Roslyn.Services"
            });

        var session = Session.Create();

        scriptEngine.Execute(varStatementText, session);

        scriptEngine.Execute("Console.WriteLine (statement.Format().GetFormattedRoot().GetFullText());", session);

The problem is that the "statement" printed to the console windows via the script engine execution will miss the backslashed around the "Hello {0}" string. If I add double escaping (additional \ into the parameter, Roslyn will raise compile errors about missing commas.

How may I update this code to get a syntactically correct version of what I want into the statement variable?

Upvotes: 1

Views: 1493

Answers (2)

Attila Hajdrik
Attila Hajdrik

Reputation: 212

Based on Kevin's tip on how to replace string for literals I played around and found this as a solution that works, but it raised another problem.

The solution:

        var parseStatementArgument = "var statement = Syntax.ParseStatement(\\\"Console.WriteLine (\\\\\\\"Hello {0}\\\\\\\", parameter1);\\\");";

        var st = Syntax.InvocationExpression(
                                    Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
                                        .AddArgumentListArguments(
                                            Syntax.Argument(Syntax.LiteralExpression(
                                                SyntaxKind.StringLiteralExpression,
                                                Syntax.Literal(
                                                    text: "\"" + parseStatementArgument + "\"",
                                                    value: parseStatementArgument.Replace ("\\\\\\", "\\"))
                                    )));

Now it correctly outputs a code snippet which is syntactically correct and compiles well.

The problem it raises is that I had to modify the source string and not the derived string to get the correct result. When rewriting code or generating code with Roslyn it can not be a requirement to double or triple escape string literals to make Roslyn able to deal with that correctly, maybe its a Roslyn issue, I hope that someone will shed some light on an elegant solution which works for all kind of strings.

Upvotes: 0

Kevin Pilch
Kevin Pilch

Reputation: 11615

How about switching to using verbatim string levels, and just add another level of escaping as you add the node.

Something like:

    var parseStatementArgument = @"var statement = Syntax.ParseStatement(@""Console.WriteLine (""""Hello {0}"""", parameter1);"");";
    var st = Syntax.InvocationExpression(
                                Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
                                    .AddArgumentListArguments(
                                        Syntax.Argument(Syntax.LiteralExpression(
                                            SyntaxKind.StringLiteralExpression,
                                            Syntax.Literal(
                                                text: "@\"" + parseStatementArgument.Replace("\"", "\"\"") + "\"",
                                                value: parseStatementArgument)
                                )));

Upvotes: 1

Related Questions