Discussion IFindReferencesService Example

  • Friday, December 23, 2011 8:53 AM
     
     

    Hi,

    I want to list all method calls of System.Threading.Task namespace in a given solution. What is the shortest way of doing this? I know that the shortest one is using IFindReferencesService but I didn't understand how I can use this service. Can you give an usage example? 

All Replies

  • Wednesday, December 28, 2011 1:02 AM
    Owner
     
     

    Hi - There are a couple of previous threads around IFindReferencesService that may be helpful.

    http://social.msdn.microsoft.com/Forums/en-US/roslyn/thread/85a816cb-e931-4b49-893a-abbbf38c7a38
    http://social.msdn.microsoft.com/Forums/en-AU/roslyn/thread/fe50bcf2-78c4-4da8-8963-b360dab03180

    To use the IFindReferencesService you would need to "MEF-Import" it from within your Roslyn VS Extension. This service is (currently) only accessible from within Roslyn VS Extensions (although this is likely to change as Dustin points out in the second post linked above).

    The IFindReferencesService essentially takes a Symbol and tells you all other places in the 'Solution' where this Symbol is used. This will be useful if you are looking for all references to a particular method / type - however, it may not be what you are looking for if you are looking for all references to any method / type under a particular namespace.

    You can also accomplish what you are trying to do (i.e. find all method calls from the System.Threading.Tasks namespace) by writing some code like below. The below code uses just the Roslyn Compiler APIs (SyntaxWalker) and will also work outside VS (inside a Roslyn Console Application for example).

    using System;
    using System.Linq;
    using Roslyn.Compilers;
    using Roslyn.Compilers.CSharp;

    class Program
    {
        static void Main(string[] args)
        {
            var tree = SyntaxTree.ParseCompilationUnit(
    @"using System;
    using System.Threading.Tasks;
    class Program
    {
        public static void Main()
        {
            Action a = () => {};
            var t = Task.Factory.StartNew(a);
            t.Wait();

            a = () =>
            {
                t = new Task(a);
                t.Start();
                t.Wait();
            };
            a();
        }
    }");
            var compilation = Compilation.Create("MyCompilation",
                syntaxTrees: new[] { tree },
                references: new[] { MetadataReference.Create(typeof(object).Assembly.Location) });

            if (compilation.GetDiagnostics().Any())
            {
                foreach (var diag in compilation.GetDiagnostics()) Console.WriteLine(diag);
            }
            else
            {
                var w = new Walker()
                {
                    CurrentTree = tree,
                    CurrentSemanticModel = compilation.GetSemanticModel(tree)
                };
                w.Visit(tree.Root);
            }
        }

        class Walker : SyntaxWalker
        {
            public SyntaxTree CurrentTree { get; set; }
            public SemanticModel CurrentSemanticModel { get; set; }

            private bool IsTaskMethodCall(SyntaxNode node)
            {
                var retVal = false;
                if (CurrentTree != null && CurrentSemanticModel != null)
                {
                    SemanticInfo semanticInfo = null;
                    if (node is ExpressionSyntax)
                    {
                        semanticInfo = CurrentSemanticModel.GetSemanticInfo((ExpressionSyntax)node);
                    }
                    else if (node is ConstructorInitializerSyntax)
                    {
                        semanticInfo = CurrentSemanticModel.GetSemanticInfo((ConstructorInitializerSyntax)node);
                    }
                    if (semanticInfo != null && semanticInfo.Symbol.Kind != SymbolKind.ErrorType)
                    {
                        var ns = semanticInfo.Symbol.ContainingNamespace.ToDisplayString();
                        if (ns == "System.Threading.Tasks")
                        {
                            Console.WriteLine("Line {0}: ... {1} ...",
                                CurrentTree.GetLineSpan(node.Span, false).StartLinePosition.Line,
                                node.GetText());
                            retVal = true;
                        }
                    }
                }
                return retVal;
            }

            protected override void VisitConstructorInitializer(ConstructorInitializerSyntax node)
            {
                IsTaskMethodCall(node);
                base.VisitConstructorInitializer(node);
            }

            protected override void VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
            {
                IsTaskMethodCall(node);
                base.VisitObjectCreationExpression(node);
            }

            protected override void VisitInvocationExpression(InvocationExpressionSyntax node)
            {
                IsTaskMethodCall(node);
                base.VisitInvocationExpression(node);
            }
        }
    }


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

  • Wednesday, December 28, 2011 2:34 AM
    Owner
     
     

    Here's an example that shows how to MEF-Import and use IFindReferencesService from within a Roslyn 'Code Issue' VS Extension. The example basically implements a ICodeIssueProvider that will put a squiggle under calls to any System.Threading.Tasks method.

    While this shows how to MEF-import and use IFindReferencesService, this is probably not the right way to accomplish what you are trying to do :)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Threading;
    using Roslyn.Compilers;
    using Roslyn.Compilers.Common;
    using Roslyn.Services;
    using Roslyn.Services.Editor;
    using Roslyn.Compilers.CSharp;

    namespace CodeIssue1
    {
        [ExportSyntaxNodeCodeIssueProvider("CodeIssue1", LanguageNames.CSharp,
            typeof(InvocationExpressionSyntax), typeof(ObjectCreationExpressionSyntax))]
        class CodeIssueProvider : ICodeIssueProvider
        {
            private readonly ICodeActionEditFactory editFactory;

            [ImportingConstructor]
            public CodeIssueProvider(ICodeActionEditFactory editFactory)
            {
                this.editFactory = editFactory;
            }

            [Import]
            private IFindReferencesService findReferencesService;
           
            public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken)
            {
                var codeIssues = new List<CodeIssue>();
                var expr = (ExpressionSyntax)node;
                var symbol = document.GetSemanticModel().GetSemanticInfo(node).Symbol;
                var solution  = document.Project.Solution;
                var compilation = document.Project.GetCompilation();
                
                var ns = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task").ContainingNamespace;
                if (symbol != null && symbol.ContainingNamespace == ns)
                {
                    // The below commented statement should be sufficient to add a squiggle
                    // under calls to any System.Threading.Tasks method. Commenting this
                    // out so that we can see how to use IFindReferencesService...
                    // codeIssues.Add(
                    //    new CodeIssue(CodeIssue.Severity.Warning,
                    //        node.Span, "System.Threading.Task Method Call"));

                    var references = findReferencesService.FindReferences(solution, symbol);
                    foreach (var r in references)
                    {
                        foreach (var l in r.Locations)
                        {
                            if (l.SourceTree == document.GetSyntaxTree())
                            {
                                // Add a code issue that will put a squiggle under each occurance of the
                                // referenced symbol - this is good enough as an example for how to use
                                // IFindReferencesService but is not the correct way to do this because
                                // this will add the same code issue more than once for the same span.
                                codeIssues.Add(
                                    new CodeIssue(CodeIssue.Severity.Warning,
                                        l.SourceSpan, "System.Threading.Task Method Call"));
                            }
                        }
                    }
                }          
                return codeIssues;
            }

            #region Unimplemented ICodeIssueProvider members
            public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxToken token, CancellationToken cancellationToken)
            {
                throw new NotImplementedException();
            }

            public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxTrivia trivia, CancellationToken cancellationToken)
            {
                throw new NotImplementedException();
            }
            #endregion
        }
    }

     


    Shyam Namboodiripad | Software Development Engineer in Test | Roslyn Compilers Team
  • Wednesday, December 28, 2011 5:01 AM
     
      Has Code

    I really appreciate your code snippet, Shyam. What I am doing is analyzing more than 500 projects in order to answer some research questions in terms of concurrency/parallelism. I try to understand how developers use TPL.

    In the below code, I find the method calls of System.Threading.Tasks namespace. But, I have some problems. First one is that I get "outofmemory exception" after analyzing approximately 100 projects. Therefore, I run below code from a batch file for every 100 projects. There shouldn't be any memory leak.

    Second one is that my program halts for some invocationexpression nodes. It doesn't throw any exception but it only waits for something, I don't know. For example,  when my program gets semantic info for "session.delete" or "this.AsDynamic().apply", it halts. How can I make my program more fault tolerant? Also, do you have some tips for improving the below code? 

    I tried your first code which is a console application. Compilation diagnosis makes the execution much slower. Many source codes don't pass this diagnosis. Also, I get null reference exception for "if (semanticInfo != null && semanticInfo.Symbol.Kind != SymbolKind.ErrorType) 

     

    Thanks in advance.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Roslyn.Compilers;
    using Roslyn.Compilers.CSharp;
    using Roslyn.Services;
    using System.IO;
    using System.Threading.Tasks;
    using System.Collections;
    using Roslyn.Compilers.Common;
    
    
    namespace Walker
    {
    
        class Program
        {
            static void Main(string[] args)
            {
    
    
                string sourceDir = @"W:\\";
                // Process the list of projects found in the directory. 
                string[] dirs = Directory.GetDirectories(sourceDir);
    
    
                foreach (string dir in dirs)
                {
                    findSolutions(dir,i);
                }
            
    
                Console.WriteLine("All done");
                Console.ReadLine();
    
    
            }
    
            private static void findSolutions(string dir, int i)
            {
    
    
                string[] solutionPaths = Directory.GetFiles(dir, "*.sln",
                                              SearchOption.AllDirectories);
    
                foreach (string solutionPath in solutionPaths)
                {
                    ISolution solution = null;
                    try
                    {
                        solution = Solution.Load(solutionPath);
    
                    }
                    catch (System.Exception e)
                    {
                        //  Console.WriteLine("*******Solution Exception*********");
                      
                    if (solution != null)
                    {
                        processSolution(solution);
                    }
                }
    
    
            }
    
            private static void processSolution(ISolution solution)
            {
                // Console.WriteLine(solution.Id.Name);
                foreach (var project in solution.Projects)
                {
                    if (project.LanguageServices.Language == "C#")
                    {
                        try
                        {
    
                            foreach (var document in project.Documents)
                            {
                                try
                                {
                                   
                                        processDocument(document);
    
                                }
                                catch (System.NotSupportedException e)
                                {
                                    //   Console.WriteLine("*******Document Exception*********");
                                        
                                catch (System.ArgumentException e)
                                {
                                   //   Console.WriteLine("*******Document Exception*********");
                                }
                            }
                        }
                        catch (Microsoft.Build.Exceptions.InvalidProjectFileException e)
                        {
    
                            // Console.WriteLine("*******Project Not Found Exception*********");
                           
                        }
                        catch (System.ArgumentException e)
                        {
                           // Console.WriteLine("*******Project Not Found Exception*********");
                        }
                        catch (System.FormatException e)
                        { 
                           // Console.WriteLine("*******Project Not Found Exception*********");
                        }
                    }
                }
            }
    
            private static void processDocument(IDocument document)
            {
                var semanticModel = document.GetSemanticModel();
                var syntaxTree = document.GetSyntaxTree();
    
                var walker = new MyWalker((SemanticModel)semanticModel);
                walker.Visit(((SyntaxTree)syntaxTree).Root);
            }
    
    
    
    
            class MyWalker : SyntaxWalker
            {
                private SemanticModel semanticModel;
                public MyWalker(SemanticModel semantic)
                {
                    this.semanticModel = semantic;
                }
    
                protected override void VisitInvocationExpression(InvocationExpressionSyntax node)
                {
      
                    try
                    {
    
                         
       
                            var semanticInfo = semanticModel.GetSemanticInfo(node);
                            var sym = semanticInfo.Symbol;
                            if (sym != null && sym.ContainingNamespace.Name == "Tasks" && sym.ContainingNamespace.ContainingNamespace != null && sym.ContainingNamespace.ContainingNamespace.Name == "Threading")
                            {
                                string line = String.Format("{0,30} {1,20} {2,20}",
                                         sym.Name,
                                         sym.ContainingType.Name,
                                         sym.ContainingNamespace.ContainingNamespace.ContainingNamespace.Name
                                         );
                                Console.WriteLine(line);
                                
                            }
                        
    
                    }
                    catch (System.ArgumentOutOfRangeException e)
                    {
                        Console.WriteLine("*******Argument Exception");
                       
                    }
                    catch (System.NullReferenceException e)
                    {
                        Console.WriteLine("*******NullReference Exception");
    
                    
                    }
                }
    
    
            }
        }
    }
    
    


    • Edited by kernell32 Wednesday, December 28, 2011 5:48 AM
    •  
  • Tuesday, January 03, 2012 7:08 PM
     
     

    In order to represent semantic models the chain of dependent compilations must exist.  The pausing could be caused by the solution object needing to compute dependent compilations and symbols (partially compiling or loading them).

    As for running out of memory, this shouldn't happen. The solution object manages the amount of text and related compilations/assemblies it keeps loaded, booting them out for the GC to reclaim when necessary.  Do you have the ability to debug or trace the memory allocations?  Any information on what is happening on your end would help us fix this bug.


    Wayward LINQ Lacky