References
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 IDeclaredElement
. This allows for cross-language refactoring, navigation and analysis, e.g. a XAML file can refer to C# code, or web pages refer to JavaScript or CSS classes.
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 T
into 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.