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)
- Iterate through all nodes in
SemanticModel.GetRoot()
and collect Expressions and LocalDeclarations - Get the symbol of the corresponding Identifier
- Check if it implements
IDisposable
, store inHashSet1
if it does - Iterate through Invocations where the method invoked is Dispose
- Get the symbol from Invocation, store in
HashSet2
- Report Diagnostic on the first
SyntaxReference
of Symbols that are inHashSet1
but not inHashSet2
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
How to Sort an Observable Collection
Cannot Access a Disposed Object in ASP.NET Core When Injecting Dbcontext
Configure JSON.Net to Ignore Datacontract/Datamember Attributes
The Provided Uri Scheme 'Https' Is Invalid; Expected 'Http'. Parameter Name: Via
Using Case/Switch and Gettype to Determine the Object
Performance of Calling Delegates VS Methods
The Type Must Be a Reference Type in Order to Use It as Parameter 'T' in the Generic Type or Method
C# Validating Input for Textbox on Winforms
One to One Optional Relationship Using Entity Framework Fluent API
Navigation Property Should Be Virtual - Not Required in Ef Core
Getting Serial Port Information
"There Was an Error Running the Selected Code Generator" in VS 2013 Scaffolding
How to Handle Session End in Global.Asax
Does Entity Framework Code First Support Stored Procedures
Comparing Strings with Tolerance
How to Use Microsoft.Office.Interop.Excel on a MAChine Without Installed Ms Office