Finding All References to a Method with Roslyn

Finding all references to a method with Roslyn

You're probably looking for the SymbolFinder class and specifically the FindAllReferences method.

It sounds like you're having some trouble getting familiar with Roslyn. I've got a series of blog posts to help people get introduced to Roslyn called Learn Roslyn Now.

As @SLaks mentions you're going to need access to the semantic model which I cover in Part 7: Introduction to the Semantic Model

Here's a sample that shows you how the API can be used. If you're able to, I'd use MSBuildWorkspace and load the project from disk instead of creating it in an AdHocWorkspace like this.

var mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var ws = new AdhocWorkspace();
//Create new solution
var solId = SolutionId.CreateNewId();
var solutionInfo = SolutionInfo.Create(solId, VersionStamp.Create());
//Create new project
var project = ws.AddProject("Sample", "C#");
project = project.AddMetadataReference(mscorlib);
//Add project to workspace
ws.TryApplyChanges(project.Solution);
string text = @"
class C
{
void M()
{
M();
M();
}
}";
var sourceText = SourceText.From(text);
//Create new document
var doc = ws.AddDocument(project.Id, "NewDoc", sourceText);
//Get the semantic model
var model = doc.GetSemanticModelAsync().Result;
//Get the syntax node for the first invocation to M()
var methodInvocation = doc.GetSyntaxRootAsync().Result.DescendantNodes().OfType<InvocationExpressionSyntax>().First();
var methodSymbol = model.GetSymbolInfo(methodInvocation).Symbol;
//Finds all references to M()
var referencesToM = SymbolFinder.FindReferencesAsync(methodSymbol, doc.Project.Solution).Result;

Roslyn - find all references to method with invocation details (concrete generic arguments)

So the ReferenceLocation has two properties, a Document and a Location. The document lets you fetch the SyntaxTree and SemanticModel. When you have the SyntaxTree, you can call FindNode and pass in Location.SourceSpan, and that should get you the invocation node. From there you can call SemanticModel.GetSymbolInfo and that will give you a struct that contains the method symbol, and from there you can take a look at the type arguments.

Roslyn - How can I get all references of a variable at DiagnosticAnalyzer class?

I implemented a similar Analyzer, the algorithm was the following (it ran for ~31ms on a 2000+ line Document, but almost always under 10 ms)

  1. Iterate through all nodes in SemanticModel.GetRoot() and collect Expressions and LocalDeclarations
  2. Get the symbol of the corresponding Identifier
  3. Check if it implements IDisposable, store in HashSet1 if it does
  4. Iterate through Invocations where the method invoked is Dispose
  5. Get the symbol from Invocation, store in HashSet2
  6. Report Diagnostic on the first SyntaxReference of Symbols that are in HashSet1 but not in HashSet2

This works well, but sadly I do not know if there is a more efficient / cleaner way of doing this. I can share parts of the code if you like.

Find all references in Visual Studio solution using Roslyn

Take a look on this and this questions, they will help with indexers.

Determine namespaces - it's a bit more difficult.
You can determine it using code like

int spanStart = symbol.Locations[0].Location.SourceSpan.Start;
Document doc = symbol.Locations[0].Location.Document;
var indexerInvokation = doc.GetSyntaxRootAsync().Result.DescendantNodes()
.FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart );

After that just find indexerInvokation parents nodes until MethodDeclarationSyntax, ClassDeclarationSyntax, etc.

Upd1.
Test project code:

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int test0 = new A().GetInt();
int test1 = new IndexedUno()[2];
int test2 = new IndexedDo()[2];
}
}

public interface IIndexed
{
int this[int i] { get; }
}

public class IndexedUno : IIndexed
{
public int this[int i] => i;
}

public class IndexedDo : IIndexed
{
public int this[int i] => i;
}

public class A
{
public int GetInt() { return new IndexedUno()[1]; }
}

public class B
{
public int GetInt() { return new IndexedDo()[4]; }
}
}

Search code:

using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;

namespace AnalyzeIndexers
{
class Program
{
static void Main(string[] args)
{
string solutionPath = @"PathToSolution.sln";
var msWorkspace = MSBuildWorkspace.Create();
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;

foreach (var project in solution.Projects.Where(p => p.AssemblyName.StartsWith("TestApp")))
{
var compilation = project.GetCompilationAsync().Result;
var interfaceType = compilation.GetTypeByMetadataName("TestApp.IIndexed");
var indexer = interfaceType
.GetMembers()
.OfType<IPropertySymbol>()
.First(member => member.IsIndexer);

var indexReferences = SymbolFinder.FindReferencesAsync(indexer, solution).Result.ToList();

foreach (var indexReference in indexReferences)
{
foreach (ReferenceLocation indexReferenceLocation in indexReference.Locations)
{
int spanStart = indexReferenceLocation.Location.SourceSpan.Start;
var doc = indexReferenceLocation.Document;

var indexerInvokation = doc.GetSyntaxRootAsync().Result
.DescendantNodes()
.FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart);

var className = indexerInvokation.Ancestors()
.OfType<ClassDeclarationSyntax>()
.FirstOrDefault()
?.Identifier.Text ?? String.Empty;

var @namespace = indexerInvokation.Ancestors()
.OfType<NamespaceDeclarationSyntax>()
.FirstOrDefault()
?.Name.ToString() ?? String.Empty;

Console.WriteLine($"{@namespace}.{className} : {indexerInvokation.GetText()}");
}
}
}

Console.WriteLine();
Console.ReadKey();

}
}
}

Take a look at the var indexer = ... code - it extracts indexer from a type. Maybe you'll need to work with getter\setter.

Other point of interest: indexerInvokation computation. We get SyntaxRoot too often, maybe you'll need some kind of cache.

Next: class and namespace search. I didn't find a method, but recommend not to find it: there can be properties, other indexers, anonymous methods used your indexers. If you don't really care about this - just find ancestors of type MethodDeclarationSyntax.



Related Topics



Leave a reply



Submit