diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs index ee601d73a63912e3fa6bd802ded7e4ac94b64f96..2f9f06d69bf718bb62773bb10bb3a756d253a201 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -15,6 +18,8 @@ namespace Microsoft.CodeAnalysis.FindSymbols { using SymbolAndProjectIdSet = HashSet>; + using RelatedTypeCache = ConditionalWeakTable>>>>; + /// /// Provides helper methods for finding dependent types (derivations, implementations, /// etc.) across a solution. The results found are returned in pairs of s @@ -38,17 +43,44 @@ internal static class DependentTypeFinder private static readonly ObjectPool s_setPool = new ObjectPool( () => new SymbolAndProjectIdSet(SymbolAndProjectIdComparer.SymbolEquivalenceInstance)); + // Caches from a types to their related types (in the context of a specific solution). + // Kept as a cache so that clients who make many calls into us won't end up computing + // the same data over and over again. Will be let go the moment the solution they're + // based off of is no longer alive. + + private static readonly RelatedTypeCache s_typeToImmediatelyDerivedClassesMap = new RelatedTypeCache(); + private static readonly RelatedTypeCache s_typeToTransitivelyDerivedClassesMap = new RelatedTypeCache(); + private static readonly RelatedTypeCache s_typeToTransitivelyImplementingTypesMap = new RelatedTypeCache(); + private static readonly RelatedTypeCache s_typeToImmediatelyDerivedAndImplementingTypesMap = new RelatedTypeCache(); + + public static async Task>> FindTypesFromCacheOrComputeAsync( + INamedTypeSymbol type, + Solution solution, + RelatedTypeCache cache, + Func>>> findAsync, + CancellationToken cancellationToken) + { + var dictionary = cache.GetOrCreateValue(solution); + var lazy = dictionary.GetOrAdd(type, new AsyncLazy>>( + asynchronousComputeFunction: findAsync, cacheResult: true)); + + return await lazy.GetValueAsync(cancellationToken).ConfigureAwait(false); + } + /// /// Used for implementing the Inherited-By relation for progression. /// - internal static Task>> FindImmediatelyDerivedClassesAsync( + public static Task>> FindImmediatelyDerivedClassesAsync( INamedTypeSymbol type, Solution solution, CancellationToken cancellationToken) { - return FindDerivedClassesAsync( - SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, - transitive: false, cancellationToken: cancellationToken); + return FindTypesFromCacheOrComputeAsync( + type, solution, s_typeToImmediatelyDerivedClassesMap, + c => FindDerivedClassesAsync( + SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, + transitive: false, cancellationToken: c), + cancellationToken); } /// @@ -60,9 +92,12 @@ internal static class DependentTypeFinder IImmutableSet projects, CancellationToken cancellationToken) { - return FindDerivedClassesAsync( - SymbolAndProjectId.Create(type, projectId: null), solution, projects, - transitive: true, cancellationToken: cancellationToken); + return FindTypesFromCacheOrComputeAsync( + type, solution, s_typeToTransitivelyDerivedClassesMap, + c => FindDerivedClassesAsync( + SymbolAndProjectId.Create(type, projectId: null), solution, projects, + transitive: true, cancellationToken: c), + cancellationToken); } private static Task>> FindDerivedClassesAsync( @@ -95,7 +130,19 @@ internal static class DependentTypeFinder /// Implementation of for /// s /// - public static async Task>> FindTransitivelyImplementingTypesAsync( + public static Task>> FindTransitivelyImplementingTypesAsync( + INamedTypeSymbol type, + Solution solution, + IImmutableSet projects, + CancellationToken cancellationToken) + { + return FindTypesFromCacheOrComputeAsync( + type, solution, s_typeToTransitivelyImplementingTypesMap, + c => FindTransitivelyImplementingTypesWorkerAsync(type, solution, projects, c), + cancellationToken); + } + + private static async Task>> FindTransitivelyImplementingTypesWorkerAsync( INamedTypeSymbol type, Solution solution, IImmutableSet projects, @@ -113,14 +160,17 @@ internal static class DependentTypeFinder /// /// Used for implementing the Inherited-By relation for progression. /// - internal static Task>> FindImmediatelyDerivedAndImplementingTypesAsync( + public static Task>> FindImmediatelyDerivedAndImplementingTypesAsync( INamedTypeSymbol type, Solution solution, CancellationToken cancellationToken) { - return FindDerivedAndImplementingTypesAsync( - SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, - transitive: false, cancellationToken: cancellationToken); + return FindTypesFromCacheOrComputeAsync( + type, solution, s_typeToImmediatelyDerivedAndImplementingTypesMap, + c => FindDerivedAndImplementingTypesAsync( + SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, + transitive: false, cancellationToken: c), + cancellationToken); } private static Task>> FindDerivedAndImplementingTypesAsync( @@ -553,7 +603,8 @@ internal static class DependentTypeFinder var typesToSearchFor = CreateSymbolAndProjectIdSet(); typesToSearchFor.AddAll(sourceAndMetadataTypes); - var inheritanceQuery = new InheritanceQuery(sourceAndMetadataTypes); + var caseSensitive = project.LanguageServices.GetService().IsCaseSensitive; + var inheritanceQuery = new InheritanceQuery(sourceAndMetadataTypes, caseSensitive); // As long as there are new types to search for, keep looping. while (typesToSearchFor.Count > 0) @@ -562,10 +613,10 @@ internal static class DependentTypeFinder inheritanceQuery.TypeNames.AddRange(typesToSearchFor.Select(c => c.Symbol.Name)); // Search all the documents of this project in parallel. - var tasks = project.Documents.Select(d => FindImmediatelyInheritingTypesInDocumentAsync( + var tasks = project.Documents.Select(d => Task.Run(() => FindImmediatelyInheritingTypesInDocumentAsync( d, typesToSearchFor, inheritanceQuery, cachedModels, cachedInfos, - sourceTypeImmediatelyMatches, cancellationToken)).ToArray(); + sourceTypeImmediatelyMatches, cancellationToken), cancellationToken)).ToArray(); await Task.WhenAll(tasks).ConfigureAwait(false); @@ -720,13 +771,13 @@ private class InheritanceQuery public readonly HashSet TypeNames; - public InheritanceQuery(SymbolAndProjectIdSet sourceAndMetadataTypes) + public InheritanceQuery(SymbolAndProjectIdSet sourceAndMetadataTypes, bool caseSensitive) { DerivesFromSystemObject = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Object); DerivesFromSystemValueType = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_ValueType); DerivesFromSystemEnum = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Enum); DerivesFromSystemMulticastDelegate = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_MulticastDelegate); - TypeNames = new HashSet(StringComparer.OrdinalIgnoreCase); + TypeNames = caseSensitive ? new HashSet() : new HashSet(StringComparer.OrdinalIgnoreCase); } }