Reputation: 876
I want to do a simple code replacement in a C# (.NET 4.5) script in VS2013. Each @GetIt
call should be rewritten so that it is encapsulated in a lambda function:
new MyClass(@GetInt("a") * @GetInt("b"));
becomes
new MyClass( x => (x.GetInt("a") * x.GetInt("b")) )
I installed the CodeAnalysis of Roslyn to parse the scripts. I use the following call to get all tokens with the @GetInt
identifier:
var getters = CSharpSyntaxTree.ParseText("new MyClass(@GetInt("a") * @GetInt("b"));")
.GetRoot().DescendantTokens().OfType<SyntaxToken>()
.Where(x => x.Text.Equals("@GetInt"));
For @GetInt
as a simple parameter of the MyClass
constructor it works fine and using getters[i].Parent.Parent.Parent.Parent
I correctly get to the MethodDeclarationSyntax
node of the constructor.
However, adding the multiplication as in the example on the top, token for the second @GetInt
declares * GetInt("b")
as its parent (it is howevver already a MethodDeclarationSyntax
, not a parameter node) and traversing back to its parent leads to the CompilationUnitSyntax
which is the root!
This way, I get no information about the position of the second @GetInt
in the syntax tree. Consequently the replacement is not possible without the missing information.
I have checked the case without using the prefix @
, but the result was the same. Can someone please tell me whether I'm doing something wrong?
SOLUTION as suggested by JoshVarty in one of his comments: Until the code analysis of Scripts is disabled in the NuGet package, I have to use a workaround. First, I take the string script = "..."
and decorate it to
var decoratedScript = "class MYCLASS { void METHOD() {\n" + script + "\n} }";
After the rewriting of @GetInt
is done, I remove the added decorations.
Upvotes: 2
Views: 562
Reputation: 9426
By default ParseText()
expects you to be passing in a typical C# document. (Something comprised of using statements, namespaces, types etc.)
If you'd like to parse individual expressions you can use CSharpParseOptions
to do so:
var parseOptions = CSharpParseOptions.Default;
parseOptions = parseOptions.WithKind(SourceCodeKind.Script); //We're going to be passing individual expressions in.
var getters = CSharpSyntaxTree.ParseText(@"new MyClass(@GetInt(""a"") * @GetInt(""b""));", parseOptions)
.GetRoot().DescendantTokens().OfType<SyntaxToken>()
.Where(x => x.Text.Equals("@GetInt"));
When I use this, I get both invocations of @GetInt
as a child of a BinaryExpressionSyntax
(The multiplication).
You can see the tree was originally parsed incorrectly for yourself via:
var tree = CSharpSyntaxTree.ParseText(@"new MyClass(@GetInt(""a"") * @GetInt(""b""));");
var errs = tree.GetDiagnostics().Where(n => n.Severity == DiagnosticSeverity.Error);
Edit Really sorry, it appears that the scripting APIs were removed for RTM. The only work around I can think of for you to try is to wrap every statement in a class and a method until this starts working again.
The reason it's present on http://sourceroslyn.io is because that's based off the current master and they're re-adding it in.
Upvotes: 2