1

I am trying to use Roslyn to identify the symbol behind a method group reference. This worked fine using SemanticModel.GetSymbolInfo in earlier versions of Roslyn, but in the latest version no symbol is found. What is the right way to perform this mapping?

var ws = new AdhocWorkspace();
var proj = ws.AddProject("test", "C#")
    .AddMetadataReference(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
proj = proj.WithParseOptions(proj.ParseOptions.WithFeatures(new Dictionary<string, string> { { "IOperation", "true" }}));
var doc = proj.AddDocument("test.cs", SourceText.From(@"namespace Test {
    public class Program
    {
        public static void Main()
        {
            Func<int> x = Foo; // want to map Foo to one of the methods below!
        }

        private static int Foo() => 7;

        private static int Foo(int x) => 8
    }
}"));
proj = doc.Project;

var compilation = proj.GetCompilationAsync().Result;
var tree = doc.GetSyntaxTreeAsync().Result;
var model = compilation.GetSemanticModel(tree);
var fooToken = tree.GetRoot().DescendantTokens()
    .First(t => t.Text.ToString() == "Foo");

Console.WriteLine(model.GetSymbolInfo(fooToken.Parent).Symbol); // null
Console.WriteLine(model.GetMemberGroup(fooToken.Parent).Length); // 2

As the example, shows GetMemberGroup does return results, but it returns results for both Foo() methods rather than the specific one I'm referencing.

How can I extract the exact referenced symbol?

ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152

1 Answers1

2

There are several syntax errors in the code below.

namespace Test
{
    public class Program
    {
        public static void Main()
        {
            Func<int> x = Foo; // want to map Foo to one of the methods below!
        }

        private static int Foo() => 7;

        private static int Foo(int x) => 8
    }
}

You need to add a using for System or fully qualify Func<int> as System.Func<int>. int Foo(int x) => 8 is also missing a semicolon at the end. Due to these errors, overload resolution fails. The easiest way to check why a symbol is not selected is to look at the candidate reason like so: model.GetSymbolInfo(fooToken.Parent).CandidateReason, which will tell you that there was an overload resolution failure. You can also call compilation.GetDiagnostics() to see what errors exist in the code.

watch window with errors

If we correct these errors overload resolution will succeed

var ws = new AdhocWorkspace();
var proj = ws.AddProject("test", "C#")
                .AddMetadataReference(
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
proj = proj.WithParseOptions(proj.ParseOptions
    .WithFeatures(new Dictionary<string, string> { ["IOperation"] = "true" }));
var doc = proj.AddDocument("test.cs", SourceText.From(@"using System;
namespace Test {
    public class Program{
        public static void Main(){
            Func<int> x = Foo;
        }
        private static int Foo() => 7;
        private static int Foo(int x) => 8;
    }
}"));
proj = doc.Project;

var compilation = proj.GetCompilationAsync().GetAwaiter().GetResult();
var tree = doc.GetSyntaxTreeAsync().GetAwaiter().GetResult();
var model = compilation.GetSemanticModel(tree);
var fooToken = tree.GetRoot().DescendantTokens().First(t => t.Text == "Foo");
Console.WriteLine(model.GetSymbolInfo(fooToken.Parent).Symbol);
Console.WriteLine(model.GetMemberGroup(fooToken.Parent).Length);

porgram output watch window without errors

Jonathon Marolf
  • 2,071
  • 14
  • 16