References are a very powerful mechanism that allow any tree node to link to a declared element. The reference might be from a type name in a variable declaration, and would link to the declared element of that type. ReSharper makes ubiquitous use of references, and enables many of ReSharper’s features. For example, Ctrl+Click navigation is simply a matter of following a reference, Find Usages can be performed by listing all incoming references on a declared element, and renaming can be accomplished by renaming a declared element’s declaration, and all incoming references.
Most powerfully, references are language agnostic - they are simply a reference from an
ITreeNode to an
A reference must be resolved before use, and can return either zero, one or more declared elements. Returning a single element means the reference is resolved successfully. When zero elements are returned, this is an error, and ReSharper will highlight the tree node that owns the reference as an error. This is likely if e.g. the reference is intended to be a method call to a method that hasn’t yet been written. Resolving to more than one element typically means an error - perhaps trying to call a method overload without providing enough information as to which overload.
When resolving, two values are returned for each result or candidate result - a declared element, and a substitution. A declared element represents a semantic view of a declaration, and cannot represent all usages. For CLR generic types, to be able to provide full information about the target of a reference, a declared element must be coupled with an instance of
ISubstitution, which provides information for substituting type parameters for actual types. For example, a reference that targets
List<string> must return a declared element that represents
List<T> and a substitution that converts
string. For reference targets that don’t require substitutions, the
EmptySubstitution is used.
References can also provide a symbol table for completion, providing all of the candidate values that are possible at that location. For example, ASP.NET MVC support adds a reference where an MVC action can be used. The generated symbol table returns a list of known actions that are valid at that point.
References can be defined directly on an
ITreeNode, and are known as “first class references”. These are references that are syntax dependent, rather than semantic. A simple example of a syntactic reference is the reference added to the type usage
Foo in the expression
Foo f = GetFoo(). This can be handled purely by looking at the syntax (it’s obvious it’s a type usage), and so can be handled by the tree node directly, and is a first class reference.
ReSharper also support reference providers, which can examine the code at a higher level, and provide references based on semantic information. The MVC action sample provided above is a good example, as this requires knowing that a particular string literal argument in a method call should be the name of an MVC action.