Question

I have been experimenting with the Roslyn API in a genetic programming type of scenario. It appears like a great way to do that type of programming, but the actual implementation of simple scenarios doesn't seem very straightforward, which means I probably don't have a good understanding of how to use this API properly. Here is a simple program I am trying to modify in my experiments:

string treeText = @"using System;
                                using System.Collections.Generic;

                                namespace TestProgram
                                {
                                    class Program
                                    {
                                        static void Main(string[] args)
                                        {
                                            var myVar = 3;
                                            string myString = ""Hello World"";
                                            List<string> stringList = new List<string>();
                                            Console.WriteLine(myString + myVar);
                                            Console.ReadLine();
                                        }
                                    }
                                }";

SyntaxTree tree = SyntaxTree.ParseText(treeText);

var compilation = Compilation.Create("test.exe",
        syntaxTrees: new[] { tree },
        references: new[]
            {
                new MetadataFileReference(typeof(object).Assembly.Location),
                new MetadataFileReference(typeof(Enumerable).Assembly.Location),
            });

        SemanticModel model = compilation.GetSemanticModel(tree);

Just as a simple example let's say I somehow "randomly" decided I want to insert a new method invocation using the myString instance. What would be an efficient way to figure out what methods I could invoke off of that instance? What would then be the best way to create the necessary MethodInvocationSyntax (once I chose a specific method to use) from the symbol information? I found a method called ResolveOverloads on the SemanticModel class which appears where I need to end up, but I am having some difficulty figuring out an efficient path to the parameters this method requires. Is that even the right path to go down?

Was it helpful?

Solution

First, get the VariableDeclaratorSyntax for your variable, for example:

var variable = tree.GetRoot().DescendantNodes()
                   .OfType<VariableDeclaratorSyntax>()
                   .Single(v => v.Identifier.ValueText == "myString");

Next, get the LocalSymbol for this variable from the SemanticModel:

var variableSymbol = (LocalSymbol)model.GetDeclaredSymbol(variable);

Then you can get a list of methods you can invoke on this variable based on its type. You can either simply get all members of the type that are instance methods:

var methods =
    variableSymbol.Type.GetMembers()
                  .OfType<MethodSymbol>()
                  .Where(m => !m.IsStatic && m.MethodKind == MethodKind.Ordinary);

Or, if you wanted to include extension methods, you could use LookupSymbols():

var methods = model.LookupSymbols(
    variable.GetLocation().SourceSpan.Start, variableSymbol.Type,
    options: LookupOptions.IncludeExtensionMethods)
                   .Where(m => !m.IsStatic);

You can then choose one of the available methods based on your logic and create InvocationExpressionSyntax (the following code assumes it's a parameterless method):

var invocationExpression =
    Syntax.InvocationExpression(
        Syntax.MemberAccessExpression(
            SyntaxKind.MemberAccessExpression,
            Syntax.IdentifierName(variableSymbol.Name),
            (SimpleNameSyntax)Syntax.ParseName(selectedMethod.Name)));

You would then need to figure out where in your method to add the expression and what to do with its result (if anything).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top