How to get at local variables defined within a method body... MethodSymbol.GetMembers() doesn't exist

คำตอบ How to get at local variables defined within a method body... MethodSymbol.GetMembers() doesn't exist

  • 2 มีนาคม 2555 16:22
     
     

    In recursively powering through an Assembly to put together a "flat" symbol table, I've run into a frustrating issue: there doesn't seem to be any way to grab symbols for variables defined locally within a method body.

    Grabbing them from classes is simple, using (NamedType)a.GetMembers();, but there doesn't seem to be any equivalent for MethodSymbols.

    Does anybody know how I can achieve this?


    Edit: Calling GetMembers() on the parent class does not return any LocalSymbols.
    • แก้ไขโดย iCasperZen 2 มีนาคม 2555 17:20
    •  

ตอบทั้งหมด

  • 2 มีนาคม 2555 21:52
    เจ้าของ
     
     คำตอบ มีโค้ด

    Hi - Could you please elaborate  a little on the scenario / problem you are trying to solve? What are you using the 'flat' symbol table for?

    I may be wrong, but I believe the reason locals are not exposed directly through the Symbols API is because unlike methods, properties etc. that are available both from source and from metadata, locals are only available from source and are only meaningful in the context of the method sources / syntax.

    You should be able to use SemanticModel.GetDeclaredSymbols() to get the LocalSymbols i.e. if the MethodSymbol is declared in source then find the corresponding MethodDeclarationSyntax using MethodSymbol.Location and use SemanticModel.GetDeclaredSymbol() on each node of type VariableDeclaratorSyntax inside this MethodDeclarationSyntax to get the corresponding LocalSymbols.

    Note: Below code is just a quick example - I haven't spent much time trying to handle all possible kinds of methods / locals :) More work may be needed to make it work correctly for all cases. Also, the example only shows how to get LocalSymbols for locals declared within methods - you would need more code to handle Properties etc. similarly...

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Roslyn.Compilers;
    using Roslyn.Compilers.CSharp;
     
    class Program
    {
        static void Main(string[] args)
        {
            var source = @"
    using System;
    using System.Diagnostics;
    class C
    {
        public C()
        {
            var a = 0;
     
            for(int b = 0; b < 1; b++)
            {
                var c = a + (int) b;
            }
     
            var d = 0;
            Console.WriteLine(d);
        }
        static void Main()
        {
            int a = 0, b = a; a = b;
        }
    }
    struct D
    {
        public void M1()
        {
            var a = 0;
            
            {
                var b = a;
            }
     
            var c = 0;
            Console.WriteLine(c);
        }
    }";
            var tree = SyntaxTree.ParseCompilationUnit(source);
            var mscorlib = new AssemblyFileReference(typeof(object).Assembly.Location);
            var comp = Compilation.Create("MyCompilation", syntaxTrees: new[] { tree }, references: new[] { mscorlib });
            var model = comp.GetSemanticModel(tree);
     
            foreach (var type in comp.Assembly.GlobalNamespace.GetTypeMembers())
            {
                foreach (var member in type.GetMembers())
                {
                    if (!member.IsSynthesized && member is MethodSymbol)
                    {
                        var methodSymbol = (MethodSymbol)member;
                        var location = methodSymbol.Locations.FirstOrDefault();
                        if (location != null && location.InSource)
                        {
                            var methodIdentifierToken = location.SourceTree.Root.FindToken(location.SourceSpan.Start);
                            var methodDeclarationNode = methodIdentifierToken.Parent;
                            var walker = new Walker(model);
                            walker.Visit(methodDeclarationNode);
                            if (walker.Results.Any())
                            {
                                var locals = string.Empty;
                                foreach (var local in walker.Results)
                                {
                                    locals += local.Name + ",";
                                }
     
                                Console.WriteLine("Method " + methodSymbol.Name + " defines locals " + locals.TrimEnd(','));
                            }
                        }
                    }
                }
            }
        }
     
        class Walker : SyntaxWalker
        {
            private SemanticModel model;
     
            public Walker(SemanticModel model)
            {
                this.model = model;
                Results = new List<Symbol>();
            }
     
            public List<Symbol> Results { getprivate set; }
     
            protected override void VisitVariableDeclarator(VariableDeclaratorSyntax node)
            {
                Results.Add(model.GetDeclaredSymbol(node));
            }
        }
    }
    

    Hope this helps!


    Shyam Namboodiripad | Software Development Engineer in Test | Roslyn Compilers Team

  • 12 มีนาคม 2555 14:56
     
     

    Thanks for the detailed reply! It has been very useful!

    The "flat" symbol table is a purely academic (and probably misguided) effort to provide a quick model for easy implementation of basic refactorings.