-
Notifications
You must be signed in to change notification settings - Fork 4k
/
ReferenceLocation.cs
163 lines (139 loc) · 6.53 KB
/
ReferenceLocation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols
{
/// <summary>
/// Information about a reference to a symbol.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
public readonly struct ReferenceLocation : IComparable<ReferenceLocation>, IEquatable<ReferenceLocation>
{
/// <summary>
/// The document that the reference was found in.
/// </summary>
public Document Document { get; }
/// <summary>
/// If the symbol was bound through an alias, then this is the alias that was used.
/// </summary>
public IAliasSymbol? Alias { get; }
/// <summary>
/// The actual source location for a given symbol.
/// </summary>
public Location Location { get; }
/// <summary>
/// Indicates if this is an implicit reference to the definition. i.e. the definition wasn't
/// explicitly stated in the source code at this position, but it was still referenced. For
/// example, this can happen with special methods like GetEnumerator that are used
/// implicitly by a 'for each' statement.
/// </summary>
public bool IsImplicit { get; }
/// <summary>
/// Indicates if this is a location where the reference is written to.
/// </summary>
internal bool IsWrittenTo => SymbolUsageInfo.IsWrittenTo();
/// <summary>
/// Symbol usage info for this reference.
/// </summary>
internal SymbolUsageInfo SymbolUsageInfo { get; }
/// <summary>
/// Additional properties for this reference
/// </summary>
internal ImmutableDictionary<string, string> AdditionalProperties { get; }
/// <summary>
/// If this reference location is within a string literal, then this property
/// indicates the location of the containing string literal token.
/// Otherwise, <see cref="Location.None"/>.
/// </summary>
internal Location ContainingStringLocation { get; }
public CandidateReason CandidateReason { get; }
private ReferenceLocation(
Document document,
IAliasSymbol? alias,
Location location,
bool isImplicit,
SymbolUsageInfo symbolUsageInfo,
ImmutableDictionary<string, string> additionalProperties,
CandidateReason candidateReason,
Location containingStringLocation)
{
this.Document = document;
this.Alias = alias;
this.Location = location;
this.IsImplicit = isImplicit;
this.SymbolUsageInfo = symbolUsageInfo;
this.AdditionalProperties = additionalProperties ?? ImmutableDictionary<string, string>.Empty;
this.CandidateReason = candidateReason;
this.ContainingStringLocation = containingStringLocation;
}
/// <summary>
/// Creates a reference location with the given properties.
/// </summary>
internal ReferenceLocation(Document document, IAliasSymbol? alias, Location location, bool isImplicit, SymbolUsageInfo symbolUsageInfo, ImmutableDictionary<string, string> additionalProperties, CandidateReason candidateReason)
: this(document, alias, location, isImplicit, symbolUsageInfo, additionalProperties, candidateReason, containingStringLocation: Location.None)
{
}
/// <summary>
/// Creates a reference location within a string literal.
/// For example, location inside the target string of a global SuppressMessageAttribute.
/// </summary>
internal ReferenceLocation(Document document, Location location, Location containingStringLocation)
: this(document, alias: null, location, isImplicit: false,
SymbolUsageInfo.None, additionalProperties: ImmutableDictionary<string, string>.Empty,
CandidateReason.None, containingStringLocation)
{
}
/// <summary>
/// Indicates if this was not an exact reference to a location, but was instead a possible
/// location that was found through error tolerance. For example, a call to a method like
/// "Goo()" could show up as an error tolerance location to a method "Goo(int i)" if no
/// actual "Goo()" method existed.
/// </summary>
public bool IsCandidateLocation => this.CandidateReason != CandidateReason.None;
public static bool operator ==(ReferenceLocation left, ReferenceLocation right)
=> left.Equals(right);
public static bool operator !=(ReferenceLocation left, ReferenceLocation right)
=> !(left == right);
public override bool Equals(object? obj)
{
return obj is ReferenceLocation location &&
Equals(location);
}
public bool Equals(ReferenceLocation other)
{
return
EqualityComparer<IAliasSymbol>.Default.Equals(this.Alias, other.Alias) &&
EqualityComparer<Location>.Default.Equals(this.Location, other.Location) &&
EqualityComparer<DocumentId>.Default.Equals(this.Document.Id, other.Document.Id) &&
this.CandidateReason == other.CandidateReason &&
this.IsImplicit == other.IsImplicit;
}
public override int GetHashCode()
{
return
Hash.Combine(this.IsImplicit,
Hash.Combine((int)this.CandidateReason,
Hash.Combine(this.Alias,
Hash.Combine(this.Location, this.Document.Id.GetHashCode()))));
}
public int CompareTo(ReferenceLocation other)
{
int compare;
var thisPath = this.Location.SourceTree?.FilePath;
var otherPath = other.Location.SourceTree?.FilePath;
if ((compare = StringComparer.OrdinalIgnoreCase.Compare(thisPath, otherPath)) != 0 ||
(compare = this.Location.SourceSpan.CompareTo(other.Location.SourceSpan)) != 0)
{
return compare;
}
return 0;
}
private string GetDebuggerDisplay()
=> string.Format("{0}: {1}", this.Document.Name, this.Location);
}
}