Roslyn: API to find references to a symbol

Created on 9 Jun 2015  路  20Comments  路  Source: dotnet/roslyn

Multiple diagnostics require to find all references to a symbol, such as unused local variable, unused class type, and so on.

In Roslyn, SymbolFinder_References.cs enables the finding of all references from a symbol. The issue is that it requires access to a Workspace - which I understand is not really part of the API, as these are specific to either Visual Studio or MSBuild.

So, we end-up having to duplicate the following logic in quite a few places

  1. In a compilation or block start, create and initialize a temporary references structure
  2. Register to all IdentifierName nodes, call semanticModel.GetSymbolInfo() on them, and add the result to the temporary references structure
  3. In a compilation or block end, process the temporary references structure

This seems a bit hacky, and there are remaining open questions: For example, is it sufficient to register to IdentifierName, or should other kinds be covered as well to capture all possible references?

And things get even worse when we want to find references across partial classes (for example for unused class type parameters), because in that case you need to get the right SemanticModel back for each node.

0 - Backlog Area-Analyzers Feature Request

Most helpful comment

Milestone: Unknown. So I guess there is a good workaround someone could explain here? Duplicating the whole bookkeeping of a Workspace for each analyser does not seem to be a viable workaround.

All 20 comments

We've talked about adding a RegisterSymbolReferenceAction that could help here. We've had a lot of people run into this need and so we should do something here soon.

Milestone: Unknown. So I guess there is a good workaround someone could explain here? Duplicating the whole bookkeeping of a Workspace for each analyser does not seem to be a viable workaround.

I'd like to put this in the spotlight again. I could really use this for a few analyzers I've written so far.

NonEncapsulatedOrMutableFieldAnalyzer (https://github.com/VSDiagnostics/VSDiagnostics/issues/292):
I need to find all the references (in this class and outside of it) to a specific field so I can determine if it is ever passed as ref or out. If it is, I can't change it to a property (which is what the corresponding code fix does).

RedundantPrivateSetter (https://github.com/VSDiagnostics/VSDiagnostics/issues/58):
I have to find out whether the property is being set somewhere or not. Now I manually look through all the nodes in the inner class but accounting for partial classes and all that jazz becomes cumbersome.

@Vannevelj if we provided a SymbolFinder.FindReferencesAsync(symbol, compilation, cancellationToken) like so:

C# public static Task<IEnumerable<ReferencedSymbol>> FindReferencesAsync( ISymbol symbol, Compilation compilation, CancellationToken cancellationToken = default(CancellationToken))

would that help? Or do you need access to the entire workspace?

Yes, that would be perfectly fine I believe: I only care about the project that the analyzer is analyzing.

@Vannevelj well that's do-able in a reasonable amount of time. Workspace analysis is what needs proper design and thought.

@jmarolf Should that be filed as a separate issue then? I don't know if this issue is aimed at a workspace scope.

@Vannevelj lets do that. We'll use this issue to track expanding analyzer analysis to workspaces.

I don't think you want a SymbolFinder like API in the compiler layer - it's going to go through the entire compilation on demand and kill analyzer perf. I think this issue is about adding a 'RegisterSymbolReferenceAction(callback, ISymbol)`. The callback will get called whenever there's a reference to the passed in symbol. That way the references are computed as the compiler goes through the project and any cumulative actions can be taken at CompilationEnd.

馃憤 @srivatsn

It would be great if this RegisterSymbolReferenceAction could also be used within a RegisterSymbolAction (and not only on a full compilation) - for example to track only private fields or method locals references, which cannot be accessed from symbols other than the one they are declared in.

Indeed, it would be a pity if we had to report in the CompilationEnd even for these basic use cases: There is a huge delay between the moment a user changes code in the IDE and the end of the compilation, and that delay gets larger with the project. After you've introduced for example a private field that could be made readonly, it takes minutes to see your issue appear. If you keep on editing the code, it might never appear. In terms of user experience this isn't the best.

Two years later and I still can't find advertised method in the API :)
Is there any progress on this or good workaround for now?

@InflexCZE There has been no progress on this. If you have a design for a way this could be done efficiently, it would def be appreciated!

How is it done in Visual Studio right now? I thought IntelliSense uses roslyn for a while now.

As mentioned:

In Roslyn, SymbolFinder_References.cs enables the finding of all references from a symbol. The issue is that it requires access to a Workspace

Finding references is something that has to work across projects/compilations. As such, it's not a general facility provided at the Compilation layer. Instead, it's provided at the Workspace/Solution layer. The Solution is the general concept Roslyn has for talking about all your code.

Sorry, I am a bit slow. Maybe that 3 years slowed me down that this ticket is open for now. :wink: So this Roslyn SymbolFinder depends on Workspace that has no public implementation, and still Roslyn has another concept called Solution (that seems to be quite close to a workspace to me), and the idea is to make it depend on a Compilation (which is done either on a workspace/solution or a selected set of projects), but that was rejected a few years ago. And still, if I right click on a symbol in VS and look for refefences, it shows them using Roslyn, right?

So this Roslyn SymbolFinder depends on Workspace that has no public implementation,

Workspace has several public implementations. If you're in VS you can get at the public VisualStudioWorkspace. If you're on the command line, you can use MSBuildWorkspace.

and still Roslyn has another concept called Solution (that seems to be quite close to a workspace to me),

Solution is a snapshot of a Workspace at a point in time. Workspace represents the mutable entity that is changing over time as someone interacts with it (i.e. in a host like VS).Solution` is a point in time where things do not change and you can ask questions that are globally consistent.

and the idea is to make it depend on a Compilation (which is done either on a workspace/solution or a selected set of projects),

I have no idea what this means.

And still, if I right click on a symbol in VS and look for refefences, it shows them using Roslyn, right?

Yes. Roslyn powers Find-References for C#/VB/TS/JS. However, the question people are asking here is: how can find references to a symbol without using the Workspace/Solution mechanisms. Currently the answer is: "you can't". That's because Finding-References is implemented as an operation that looks at things globally, and during analysis you only have a local view of at most one-single project being compiled.

This is a really annoying downside to the roslyn analyzers. It's sad that this issue is opened for over 3 years without any progress. This is a crucial functionality for many projects and there is currently no easy workaround available. Could you please look into this again and provide a solution. Thanks!

This is a crucial functionality for many projects and there is currently no easy workaround available. Could you please look into this again and provide a solution. Thanks!

As you said, there is no easy solution here. The lack of an option isn't due to lack of interest, it's due to no one having an idea how this would be solvable without causing many other problems of its own (for example, horrible performance cost).

A year later...I'm looking to find references to local variables, is there a way of doing that at least? Or is there a way to get the solution without reflection so I can use SymbolFinder.FindReferencesAsync?

Or is there a way to get the solution without reflection so I can use SymbolFinder.FindReferencesAsync?

There are many ways to get at a Solution instance @jrmoreno1 . What is your code doing? Is it starting from a VS text-editor session, for example? I can help walk you through how to do this, but i'll need more info on your part.

Note: gitter.im/dotnet/roslyn and aka.ms/csharp-discord are good places to come discuss this sort of things. Lots of people with experience with Roslyn who can help you.

Was this page helpful?
0 / 5 - 0 ratings