PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Workspaces/Core/Portable/SymbolId/SymbolKey.cs

https://gitlab.com/sharadag/TestProject2
C# | 293 lines | 147 code | 28 blank | 118 comment | 23 complexity | 44e1fb4a2bf608a3ca5f578f29253018 MD5 | raw file
  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Immutable;
  5. using System.Linq;
  6. using System.Threading;
  7. using Microsoft.CodeAnalysis.Shared.Utilities;
  8. using Microsoft.CodeAnalysis.Text;
  9. using Roslyn.Utilities;
  10. namespace Microsoft.CodeAnalysis
  11. {
  12. /// <summary>
  13. /// <para>
  14. /// A SymbolKey is a lightweight identifier for a symbol that can be used to resolve the "same"
  15. /// symbol across compilations. Different symbols have different concepts of "same-ness".
  16. /// Same-ness is recursively defined as follows:
  17. /// <list type="number">
  18. /// <item>Two IArraySymbol's are the "same" if they have the "same" element type and the same rank.</item>
  19. /// <item>Two IAssemblySymbol's are the "same" if they have the same <see cref="ISymbol.Name"/>.</item>
  20. /// <item>Two IEventSymbol's are the "same" if they have the "same" containing type and the same <see cref="ISymbol.MetadataName"/>.</item>
  21. /// <item>Two IMethodSymbol's are the "same" if they have the "same" containing type, the same
  22. /// <see cref="ISymbol.MetadataName"/>, the same <see cref="IMethodSymbol.Arity"/>, the "same"
  23. /// <see cref="IMethodSymbol.TypeArguments"/>, and have same parameter types and
  24. /// <see cref="IParameterSymbol.RefKind"/>.</item>
  25. /// <item>IModuleSymbol's are the "same" if they have the same containing assembly.
  26. /// <see cref="ISymbol.MetadataName"/> is not used because module identity is not important in practice.</item>
  27. /// <item>Two INamedTypeSymbol's are the "same" if they have "same" containing symbol, the same
  28. /// <see cref="ISymbol.MetadataName"/>, the same <see cref="INamedTypeSymbol.Arity"/> and the "same"
  29. /// <see cref="INamedTypeSymbol.TypeArguments"/>.</item>
  30. /// <item>Two INamespaceSymbol's are the "same" if they have the "same" containing symbol and the
  31. /// same <see cref="ISymbol.MetadataName"/>. If the INamespaceSymbol is the global namespace for a
  32. /// compilation (and thus does not have a containing symbol) then it will only match another
  33. /// global namespace of another compilation.</item>
  34. /// <item>Two IParameterSymbol's are the "same" if they have the "same" containing symbol and the <see cref="ISymbol.MetadataName"/>.</item>
  35. /// <item>Two IPointerTypeSymbol's are the "same" if they have the "same" <see cref="IPointerTypeSymbol.PointedAtType"/>.</item>
  36. /// <item>Two IPropertySymbol's are the "same" if they have the "same" containing type, the same
  37. /// <see cref="ISymbol.MetadataName"/>, and have same parameter types and <see cref="IParameterSymbol.RefKind"/>.</item>
  38. /// <item>Two ITypeParameterSymbol's are the "same" if they have the "same" containing symbol and the <see cref="ISymbol.MetadataName"/>.</item>
  39. /// <item>Two IFieldSymbol's are the "same" if they have the "same" containing symbol and the <see cref="ISymbol.MetadataName"/>.</item>
  40. /// </list>
  41. /// A SymbolID for an IAliasSymbol will <see cref="SymbolKey.Resolve"/> back to the ISymbol for
  42. /// the <see cref="IAliasSymbol.Target"/>.
  43. /// </para>
  44. /// <para>
  45. /// Due to issues arising from errors and ambiguity, it's possible for a SymbolKey to resolve to
  46. /// multiple symbols. For example, in the following type:
  47. /// <code>
  48. /// class C
  49. /// {
  50. /// int Foo();
  51. /// bool Foo();
  52. /// }
  53. /// </code>
  54. /// The SymbolKey for both Foo methods will be the same. The SymbolId will then resolve to both methods.
  55. /// </para>
  56. /// </summary>
  57. internal abstract partial class SymbolKey
  58. {
  59. private static readonly SymbolKey s_null = new NullSymbolKey();
  60. public abstract SymbolKeyResolution Resolve(Compilation compilation, bool ignoreAssemblyKey = false, CancellationToken cancellationToken = default(CancellationToken));
  61. public static IEqualityComparer<SymbolKey> GetComparer(bool ignoreCase, bool ignoreAssemblyKeys)
  62. {
  63. return SymbolKeyComparer.GetComparer(ignoreCase, ignoreAssemblyKeys, compareMethodTypeParametersByName: false);
  64. }
  65. /// <summary>
  66. /// <para>
  67. /// This entry point should only be called from the actual Symbol classes. It should not be
  68. /// used internally inside this type. Instead, any time we need to get the <see cref="SymbolKey"/> for a
  69. /// related symbol (i.e. the containing namespace of a namespace) we should call
  70. /// <see cref="GetOrCreate"/>. The benefit of this is twofold. First of all, it keeps the size of the
  71. /// <see cref="SymbolKey"/> small by allowing up to reuse parts we've already created. For example, if we
  72. /// have the <see cref="SymbolKey"/> for <c>Foo(int, int)</c>, then we will reuse the <see cref="SymbolKey"/>s for both <c>int</c>s.
  73. /// Second, this allows us to deal with the recursive nature of MethodSymbols and
  74. /// TypeParameterSymbols. Specifically, a MethodSymbol is defined by its signature. However,
  75. /// it's signature may refer to type parameters of that method. Unfortunately, the type
  76. /// parameters depend on their containing method.
  77. /// </para>
  78. /// <para>
  79. /// For example, if there is <c><![CDATA[Foo<T>(T t)]]></c>, then we must avoid the situation where we:
  80. /// <list type="number">
  81. /// <item>try to get the symbol ID for the type parameter <c>T</c>, which in turn</item>
  82. /// <item>tries to get the symbol ID for the method <c>T</c>, which in turn</item>
  83. /// <item>tries to get the symbol IDs for the parameter types, which in turn</item>
  84. /// <item>tries to get the symbol ID for the type parameter <c>T</c>, which leads back to 1 and infinitely loops.</item>
  85. /// </list>
  86. /// </para>
  87. /// <para>
  88. /// In order to break this circularity we do not create the SymbolIDs for a method's type
  89. /// parameters directly in the visitor. Instead, we create the SymbolID for the method
  90. /// itself. When the MethodSymbolId is created it will directly instantiate the SymbolIDs
  91. /// for the type parameters, and directly assign the type parameter's method ID to itself.
  92. /// It will also then directly store the mapping from the type parameter to its SymbolID in
  93. /// the visitor cache. Then when we try to create the symbol IDs for the parameter types,
  94. /// any reference to the type parameters can be found in the cache.
  95. /// </para>
  96. /// <para>
  97. /// It is for this reason that it is essential that all calls to get related symbol IDs goes
  98. /// through GetOrCreate and not Create.
  99. /// </para>
  100. /// </summary>
  101. internal static SymbolKey Create(ISymbol symbol, Compilation compilation = null, CancellationToken cancellationToken = default(CancellationToken))
  102. {
  103. return GetOrCreate(symbol, new Visitor(compilation, cancellationToken));
  104. }
  105. private static SymbolKey GetOrCreate(ISymbol symbol, Visitor visitor)
  106. {
  107. if (symbol == null)
  108. {
  109. return s_null;
  110. }
  111. SymbolKey result;
  112. if (!visitor.SymbolCache.TryGetValue(symbol, out result))
  113. {
  114. result = symbol.Accept(visitor);
  115. visitor.SymbolCache[symbol] = result;
  116. }
  117. return result;
  118. }
  119. /// <summary>
  120. /// <para>
  121. /// When comparing symbols we need to handle recursion between method type parameters and
  122. /// methods. For example, if we have two methods with the signature <c><![CDATA[Foo<T>(T t)]]></c> and we
  123. /// try to test for equality we must avoid the situation where we:
  124. /// <list type="number">
  125. /// <item>First test if the methods are the same, which will in turn</item>
  126. /// <item>test if the method's parameter types are the same, which will in turn</item>
  127. /// <item>test if the type parameters are the same, which will in turn</item>
  128. /// <item>test if the methods are the same, which causes infinite recursion.</item>
  129. /// </list>
  130. /// To avoid this we distinguish the cases where we're testing if two type parameters
  131. /// actually refer to the same thing, versus type parameters being referenced by parameters.
  132. /// For example, if we have:
  133. /// <code><![CDATA[
  134. /// Foo<T>(T t)
  135. /// Bar<T>(T t)
  136. /// ]]></code>
  137. /// then clearly the type parameter <c>T</c> in <c><![CDATA[Foo<T>]]></c> is different from the type parameter <c>T</c>
  138. /// in <c><![CDATA[Bar<T>]]></c>. When testing these type parameters for equality we *will* test to see
  139. /// if they have the same parent. This will end up returning false, and so we will consider
  140. /// them different.
  141. /// </para>
  142. /// <para>
  143. /// However, when we are testing if two signatures are the same, if we hit a method type
  144. /// parameter then we only need to compare by metadataName. That's because we know we'll
  145. /// already have checked if the method and it's parents are the same, so we don't need to
  146. /// recurse through them again.
  147. /// </para>
  148. /// </summary>
  149. internal abstract bool Equals(SymbolKey other, ComparisonOptions options);
  150. internal abstract int GetHashCode(ComparisonOptions options);
  151. private static bool Equals(Compilation compilation, string name1, string name2)
  152. {
  153. return Equals(compilation.IsCaseSensitive, name1, name2);
  154. }
  155. private static bool Equals(bool isCaseSensitive, string name1, string name2)
  156. {
  157. return string.Equals(name1, name2, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
  158. }
  159. private static int GetHashCode(bool isCaseSensitive, string metadataName)
  160. {
  161. return isCaseSensitive
  162. ? metadataName.GetHashCode()
  163. : StringComparer.OrdinalIgnoreCase.GetHashCode(metadataName);
  164. }
  165. private static IEnumerable<ISymbol> GetAllSymbols(SymbolKeyResolution info)
  166. {
  167. if (info.Symbol != null)
  168. {
  169. yield return info.Symbol;
  170. }
  171. else
  172. {
  173. foreach (var symbol in info.CandidateSymbols)
  174. {
  175. yield return symbol;
  176. }
  177. }
  178. }
  179. private static IEnumerable<TType> GetAllSymbols<TType>(SymbolKeyResolution info)
  180. {
  181. return GetAllSymbols(info).OfType<TType>();
  182. }
  183. private static SymbolKeyResolution CreateSymbolInfo(IEnumerable<ISymbol> symbols)
  184. {
  185. return symbols == null
  186. ? default(SymbolKeyResolution)
  187. : CreateSymbolInfo(symbols.WhereNotNull().ToArray());
  188. }
  189. private static SymbolKeyResolution CreateSymbolInfo(ISymbol[] symbols)
  190. {
  191. return symbols.Length == 0
  192. ? default(SymbolKeyResolution)
  193. : symbols.Length == 1
  194. ? new SymbolKeyResolution(symbols[0])
  195. : new SymbolKeyResolution(ImmutableArray.Create<ISymbol>(symbols), CandidateReason.Ambiguous);
  196. }
  197. private static bool ParametersMatch(
  198. ComparisonOptions options,
  199. Compilation compilation,
  200. ImmutableArray<IParameterSymbol> parameters,
  201. RefKind[] refKinds,
  202. SymbolKey[] typeKeys,
  203. CancellationToken cancellationToken)
  204. {
  205. if (parameters.Length != refKinds.Length)
  206. {
  207. return false;
  208. }
  209. for (int i = 0; i < refKinds.Length; i++)
  210. {
  211. // The ref-out distinction is not interesting for SymbolKey because you can't overload
  212. // based on the difference.
  213. var parameter = parameters[i];
  214. if (!SymbolEquivalenceComparer.AreRefKindsEquivalent(refKinds[i], parameter.RefKind, distinguishRefFromOut: false))
  215. {
  216. return false;
  217. }
  218. // We are checking parameters for equality, if they refer to method type parameters,
  219. // then we don't want to recurse through the method (which would then recurse right
  220. // back into the parameters). So we ask that type parameters only be checked by
  221. // metadataName to prevent that.
  222. var newOptions = new ComparisonOptions(
  223. options.IgnoreCase,
  224. options.IgnoreAssemblyKey,
  225. compareMethodTypeParametersByName: true);
  226. if (!typeKeys[i].Equals(SymbolKey.Create(parameter.Type, compilation, cancellationToken), newOptions))
  227. {
  228. return false;
  229. }
  230. }
  231. return true;
  232. }
  233. private static bool SequenceEquals<T>(T[] array1, T[] array2, IEqualityComparer<T> comparer)
  234. {
  235. if (array1 == array2)
  236. {
  237. return true;
  238. }
  239. if (array1 == null || array2 == null)
  240. {
  241. return false;
  242. }
  243. return array1.SequenceEqual(array2, comparer);
  244. }
  245. private static bool SequenceEquals<T>(ImmutableArray<T> array1, ImmutableArray<T> array2, IEqualityComparer<T> comparer)
  246. {
  247. if (array1 == array2)
  248. {
  249. return true;
  250. }
  251. if (array1.IsDefault || array2.IsDefault)
  252. {
  253. return false;
  254. }
  255. return array1.SequenceEqual(array2, comparer);
  256. }
  257. private static string GetName(string metadataName)
  258. {
  259. var index = metadataName.IndexOf('`');
  260. return index > 0
  261. ? metadataName.Substring(0, index)
  262. : metadataName;
  263. }
  264. }
  265. }