Roslyn SemanticModel symbol resolution not working reliably

64 Views Asked by At

I'm writing a program that needs to analyze C# code and figure out, for each method invocation, exactly what method of what class is being called. The appropriate tools for this job would seem to be Roslyn, CSharpSyntaxTree and SemanticModel, but they are not working reliably. When I run it on real code, it finds the correct symbol in a few cases, a list of candidates including the correct symbol in a few more, but in most cases, completely fails.

For a reproducible test case, here is a distilled version of the analysis code:

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

class Program {
    static void Main(string[] _) {
        var compilation = CSharpCompilation.Create("MyCompilation")
                              .WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication))
                              .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                                             MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
                                             MetadataReference.CreateFromFile(typeof(Environment).Assembly.Location),
                                             MetadataReference.CreateFromFile(Path.Combine(
                                                 Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll")));

        var file = "Class1.cs";
        var tree = CSharpSyntaxTree.ParseText(File.ReadAllText(file), CSharpParseOptions.Default, file);
        if (tree.GetDiagnostics().Any()) {
            foreach (var diagnostic in tree.GetDiagnostics())
                Console.Error.WriteLine(diagnostic);
            Environment.Exit(1);
        }
        compilation = compilation.AddSyntaxTrees(tree);

        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetCompilationUnitRoot();
        new Walker(model).Visit(root);
    }
}

class Walker: CSharpSyntaxWalker {
    public Walker(SemanticModel model) {
        this.model = model;
    }

    public override void VisitInvocationExpression(InvocationExpressionSyntax node) {
        base.VisitInvocationExpression(node);

        var symbolInfo = model.GetSymbolInfo(node);
        Console.WriteLine(node);
        Console.WriteLine(symbolInfo.Symbol);
        Console.WriteLine(symbolInfo.CandidateSymbols.ToArray());
    }

    readonly SemanticModel model;
}

I run it on this code:

class Class1 {
    void Method1() {
        Environment.Exit(0);
    }
}

And this is the output:

Environment.Exit(0)

Microsoft.CodeAnalysis.ISymbol[]

So for a static call to the standard library, that should be easy to resolve, it finds no candidate symbols.

What am I missing?

1

There are 1 best solutions below

3
D A On

You are looking for InvocationExpressionSyntax. In your example there is no method invocation. I've used this example:

    using System;
    using System.Text;

    namespace ConsoleApplication1
    {
        class MyClass
        {
            void Method(string input)
            {
                var x = new ArgumentNullException();
            }

            void MethodB(string input)
            {
                Method("TestMe");
            }

            void MethodC(string input)
            {
                MethodB("TestYou");
            }
        }
    }

and my code is the following:

    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp.Syntax;

    class Program
    {
        static void Main(string[] _)
        {

            var file = "Class1.cs";
            var tree = CSharpSyntaxTree.ParseText(File.ReadAllText(file), CSharpParseOptions.Default, file);
            if (tree.GetDiagnostics().Any())
            {
                foreach (var diagnostic in tree.GetDiagnostics())
                    Console.Error.WriteLine(diagnostic);
                Environment.Exit(1);
            }

            var mscorlib = MetadataReference.CreateFromFile(Path.Combine(
                                                     Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll"));
            var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { mscorlib });
            var semanticModel = compilation.GetSemanticModel(tree);

            var root = tree.GetRoot();
            var objectCreationExpression = root.DescendantNodes().OfType<InvocationExpressionSyntax>().ToList();        
            var lstNodes = objectCreationExpression;
            foreach (var item in lstNodes)
            {
                new Walker(semanticModel).Visit(item);
            }

        }
    }

    class Walker : CSharpSyntaxWalker
    {
        public Walker(SemanticModel model)
        {
            this.model = model;
        }

        public override void VisitInvocationExpression(InvocationExpressionSyntax node)
        {
            base.VisitInvocationExpression(node);

            var symbolInfo = model.GetSymbolInfo(node);
            Console.WriteLine(node);
            Console.WriteLine(symbolInfo.Symbol);
            Console.WriteLine(symbolInfo.CandidateSymbols.ToArray());
        }

        readonly SemanticModel model;
    }