未验证 提交 da62ba87 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #43636 from CyrusNajmabadi/depTypeFinderOOP

Move dependent-type-finding operations out of process.
......@@ -4,19 +4,58 @@
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities.RemoteHost;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests
{
public class SymbolFinderTests : ServicesTestBase
public enum TestHost
{
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CSharp()
InProcess,
OutOfProcess,
}
[UseExportProvider]
public class SymbolFinderTests : TestBase
{
private static Solution AddProjectWithMetadataReferences(Solution solution, string projectName, string languageName, string code, MetadataReference metadataReference, params ProjectId[] projectReferences)
{
var suffix = languageName == LanguageNames.CSharp ? "cs" : "vb";
var pid = ProjectId.CreateNewId();
var did = DocumentId.CreateNewId(pid);
var pi = ProjectInfo.Create(
pid,
VersionStamp.Default,
projectName,
projectName,
languageName,
metadataReferences: new[] { metadataReference },
projectReferences: projectReferences.Select(p => new ProjectReference(p)));
return solution.AddProject(pi).AddDocument(did, $"{projectName}.{suffix}", SourceText.From(code));
}
private static TestWorkspace GetWorkspace(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
var workspace = TestWorkspace.CreateWorkspace(XElement.Parse("<Workspace></Workspace>"));
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(
workspace.Options.WithChangedOption(RemoteHostOptions.RemoteHostTest, host == TestHost.OutOfProcess)));
return workspace;
}
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CSharp(TestHost host)
{
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -53,10 +92,11 @@ public class DerivedClass : BaseClass { }
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedInterfaces_CSharp()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedInterfaces_CSharp(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -99,10 +139,11 @@ private static Project GetPortableProject(Solution solution)
private static Project GetNormalProject(Solution solution)
=> solution.Projects.Single(p => p.Name == "NormalProject");
[Fact]
public async Task ImmediatelyDerivedTypes_CSharp_AliasedNames()
[Theory, CombinatorialData]
public async Task ImmediatelyDerivedTypes_CSharp_AliasedNames(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -143,10 +184,11 @@ public class DerivedClass : Alias2 { }
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CSharp_PortableProfile7()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CSharp_PortableProfile7(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -183,10 +225,11 @@ public class DerivedClass : BaseClass { }
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_VisualBasic()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_VisualBasic(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.VisualBasic, @"
......@@ -224,10 +267,11 @@ End Namespace
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CrossLanguage()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedTypes_CrossLanguage(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -265,10 +309,11 @@ End Namespace
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedAndImplementingInterfaces_CSharp()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedAndImplementingInterfaces_CSharp(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an interface
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
......@@ -304,10 +349,11 @@ public class ImplementingClass : IBaseInterface { }
Assert.Equal(implementingClassSymbol, Assert.Single(typesThatImplementInterface));
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedInterfaces_VisualBasic()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedInterfaces_VisualBasic(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an interface
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.VisualBasic, @"
......@@ -344,10 +390,11 @@ End Namespace
Assert.Equal(implementingClassSymbol, Assert.Single(typesThatImplementInterface));
}
[Fact, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedAndImplementingInterfaces_CrossLanguage()
[Theory, CombinatorialData, WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
public async Task ImmediatelyDerivedAndImplementingInterfaces_CrossLanguage(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create portable assembly with an interface
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.VisualBasic, @"
......@@ -383,10 +430,11 @@ public class ImplementingClass : IBaseInterface { }
Assert.Equal(implementingClassSymbol, Assert.Single(typesThatImplementInterface));
}
[Fact]
public async Task DerivedMetadataClasses()
[Theory, CombinatorialData]
public async Task DerivedMetadataClasses(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"", MscorlibRef);
......@@ -413,10 +461,11 @@ public async Task DerivedMetadataClasses()
Assert.True(transitiveDerived.Count() > immediateDerived.Count());
}
[Fact]
public async Task DerivedSourceInterfaces()
[Theory, CombinatorialData]
public async Task DerivedSourceInterfaces(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"
......@@ -461,10 +510,11 @@ interface IOther { }
Assert.True(transitiveDerived.Count() > immediateDerived.Count());
}
[Fact]
public async Task ImplementingSourceTypes()
[Theory, CombinatorialData]
public async Task ImplementingSourceTypes(TestHost host)
{
var solution = new AdhocWorkspace().CurrentSolution;
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"
......@@ -511,5 +561,62 @@ struct OtherStruct { }
Assert.True(transitiveImpls.Count() > immediateImpls.Count());
}
[Theory, CombinatorialData]
public async Task ImplementingTypesDoesProduceDelegates(TestHost host)
{
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"
delegate void D();
", MscorlibRef);
// get symbols for types
var compilation = await GetNormalProject(solution).GetCompilationAsync();
var rootType = compilation.GetTypeByMetadataName("System.ICloneable");
Assert.NotNull(rootType);
var transitiveImpls = await SymbolFinder.FindImplementationsAsync(
rootType, solution, transitive: true);
var delegates = transitiveImpls.Where(i => i.TypeKind == TypeKind.Delegate);
Assert.NotEmpty(delegates); // We should find delegates when looking for implementations
Assert.True(delegates.Any(i => i.Locations.Any(loc => loc.IsInMetadata)), "We should find a metadata delegate");
Assert.Single(delegates.Where(i => i.Locations.Any(loc => loc.IsInSource))); // We should find a single source delegate
}
[Theory, CombinatorialData]
public async Task ImplementingTypesDoesProduceEnums(TestHost host)
{
using var workspace = GetWorkspace(host);
var solution = workspace.CurrentSolution;
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"
enum E
{
A, B, C,
}
", MscorlibRef);
// get symbols for types
var compilation = await GetNormalProject(solution).GetCompilationAsync();
var rootType = compilation.GetTypeByMetadataName("System.IComparable");
Assert.NotNull(rootType);
var transitiveImpls = await SymbolFinder.FindImplementationsAsync(
rootType, solution, transitive: true);
var enums = transitiveImpls.Where(i => i.TypeKind == TypeKind.Enum);
Assert.NotEmpty(enums); // We should find enums when looking for implementations
Assert.True(enums.Any(i => i.Locations.Any(loc => loc.IsInMetadata)), "We should find a metadata enum");
Assert.Single(enums.Where(i => i.Locations.Any(loc => loc.IsInSource))); // We should find a single source type
}
}
}
......@@ -27,14 +27,21 @@ namespace Microsoft.CodeAnalysis.FindSymbols
using SymbolSet = HashSet<INamedTypeSymbol>;
/// <summary>
/// Provides helper methods for finding dependent types (derivations, implementations,
/// etc.) across a solution. The results found are returned in pairs of <see cref="ISymbol"/>s
/// and <see cref="ProjectId"/>s. The Ids specify what project we were searching in when
/// we found the symbol. That project has the compilation that we found the specific
/// source or metadata symbol within. Note that for metadata symbols there could be
/// many projects where the same symbol could be found. However, we only return the
/// first instance we found.
/// Provides helper methods for finding dependent types (derivations, implementations, etc.) across a solution. This
/// is effectively a graph walk between INamedTypeSymbols walking down the inheritance hierarchy to find related
/// types based either on <see cref="ITypeSymbol.BaseType"/> or <see cref="ITypeSymbol.Interfaces"/>.
/// </summary>
/// <remarks>
/// While walking up the inheritance hierarchy is trivial (as the information is directly contained on the <see
/// cref="ITypeSymbol"/>'s themselves), walking down is complicated. The general way this works is by using
/// out-of-band indices that are built that store this type information in a weak manner. Specifically, for both
/// source and metadata types we have indices that map between the base type name and the inherited type name. i.e.
/// for the case <c>class A { } class B : A { }</c> the index stores a link saying "There is a type 'A' somewhere
/// which has derived type called 'B' somewhere". So when the index is examined for the name 'A', it will say
/// 'examine types called 'B' to see if they're an actual match'.
/// <para/>
/// These links are then continually tranversed to get the full set of results.
/// </remarks>
internal static partial class DependentTypeFinder
{
// Static helpers so we can pass delegates around without allocations.
......@@ -174,7 +181,7 @@ internal static partial class DependentTypeFinder
// We might miss a derived type in C if there's an intermediate derived type
// in B.
//
// However, say we have projects A <- B <- C <- D, only only projects A and C
// However, say we have projects A <- B <- C <- D, only projects A and C
// are passed in. There is no need to check D as there's no way it could
// contribute an intermediate type that affects A or C. We only need to check
// A, B and C
......@@ -187,7 +194,7 @@ internal static partial class DependentTypeFinder
// and the list of projects the caller wants to search, find the actual list of
// projects we need to search through.
//
// This list of projects is properly topologicaly ordered. Because of this we
// This list of projects is properly topologically ordered. Because of this we
// can just process them in order from first to last because we know no project
// in this list could affect a prior project.
var orderedProjectsToExamine = GetOrderedProjectsToExamine(
......@@ -198,7 +205,7 @@ internal static partial class DependentTypeFinder
using var _1 = GetSymbolSet(out var result);
// The current total set of matching metadata types in the descendant tree (including the initial type if it
// is from metadata). Will be used when examining new types to see if they inherit from any of htese.
// is from metadata). Will be used when examining new types to see if they inherit from any of these.
using var _2 = GetSymbolSet(out var currentMetadataTypes);
// Same as above, but contains source types as well.
......@@ -318,9 +325,7 @@ private static void AddRange(SymbolSet foundTypes, SymbolSet result)
result.Add(type);
}
private static void AddRange(
SymbolSet foundTypes, SymbolSet currentTypes,
Func<INamedTypeSymbol, bool> shouldContinueSearching)
private static void AddRange(SymbolSet foundTypes, SymbolSet currentTypes, Func<INamedTypeSymbol, bool> shouldContinueSearching)
{
// Directly enumerate to avoid IEnumerator allocations.
foreach (var type in foundTypes)
......@@ -448,7 +453,7 @@ private static void AddRange(SymbolSet foundTypes, SymbolSet result)
}
private static async Task AddDescendantMetadataTypesInProjectAsync(
SymbolSet metadataTypes,
SymbolSet currentMetadataTypes,
SymbolSet result,
Project project,
Func<INamedTypeSymbol, SymbolSet, bool> typeMatches,
......@@ -458,7 +463,7 @@ private static void AddRange(SymbolSet foundTypes, SymbolSet result)
{
Debug.Assert(project.SupportsCompilation);
if (metadataTypes.Count == 0)
if (currentMetadataTypes.Count == 0)
return;
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
......@@ -466,7 +471,7 @@ private static void AddRange(SymbolSet foundTypes, SymbolSet result)
using var _1 = GetSymbolSet(out var typesToSearchFor);
using var _2 = GetSymbolSet(out var tempBuffer);
typesToSearchFor.AddAll(metadataTypes);
typesToSearchFor.AddAll(currentMetadataTypes);
// As long as there are new types to search for, keep looping.
while (typesToSearchFor.Count > 0)
......@@ -504,7 +509,7 @@ private static void AddRange(SymbolSet foundTypes, SymbolSet result)
// to the names of the all the types that either immediately derive or
// implement that type. Because the mapping is from the simple name
// we might get false positives. But that's fine as we still use
// 'metadataTypeMatches' to make sure the match is correct.
// 'tpeMatches' to make sure the match is correct.
var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
project.Solution, reference, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false);
......@@ -546,7 +551,7 @@ private static bool TypeHasInterfaceInSet(INamedTypeSymbol type, SymbolSet set)
}
private static async Task AddDescendantSourceTypesInProjectAsync(
SymbolSet sourceAndMetadataTypes,
SymbolSet currentSourceAndMetadataTypes,
SymbolSet result,
Project project,
Func<INamedTypeSymbol, SymbolSet, bool> typeMatches,
......@@ -558,14 +563,14 @@ private static bool TypeHasInterfaceInSet(INamedTypeSymbol type, SymbolSet set)
// We're going to be sweeping over this project over and over until we reach a
// fixed point. In order to limit GC and excess work, we cache all the semantic
// models and DeclaredSymbolInfo for hte documents we look at.
// models and DeclaredSymbolInfo for the documents we look at.
// Because we're only processing a project at a time, this is not an issue.
var cachedModels = new ConcurrentSet<SemanticModel>();
using var _1 = GetSymbolSet(out var typesToSearchFor);
using var _2 = GetSymbolSet(out var tempBuffer);
typesToSearchFor.AddAll(sourceAndMetadataTypes);
typesToSearchFor.AddAll(currentSourceAndMetadataTypes);
var projectIndex = await ProjectIndex.GetIndexAsync(project, cancellationToken).ConfigureAwait(false);
......
......@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols
......@@ -14,10 +15,27 @@ namespace Microsoft.CodeAnalysis.FindSymbols
internal static partial class DependentTypeFinder
{
/// <summary>
/// This is an internal implementation of <see cref="SymbolFinder.FindDerivedClassesAsync(INamedTypeSymbol, Solution, IImmutableSet{Project}, CancellationToken)"/>, which is a publically callable method.
/// </summary>
public static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedClassesAsync(
public static async Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedClassesAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
bool transitive,
CancellationToken cancellationToken)
{
var result = await TryFindAndCacheRemoteTypesAsync(
type, solution, projects, transitive,
FunctionId.DependentTypeFinder_FindAndCacheDerivedClassesAsync,
nameof(IRemoteDependentTypeFinder.FindAndCacheDerivedClassesAsync),
cancellationToken).ConfigureAwait(false);
if (result.HasValue)
return result.Value;
return await FindAndCacheDerivedClassesInCurrentProcessAsync(
type, solution, projects, transitive, cancellationToken).ConfigureAwait(false);
}
private static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedClassesInCurrentProcessAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
......
......@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols
......@@ -14,7 +15,27 @@ namespace Microsoft.CodeAnalysis.FindSymbols
internal static partial class DependentTypeFinder
{
public static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedInterfacesAsync(
public static async Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedInterfacesAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
bool transitive,
CancellationToken cancellationToken)
{
var result = await TryFindAndCacheRemoteTypesAsync(
type, solution, projects, transitive,
FunctionId.DependentTypeFinder_FindAndCacheDerivedInterfacesAsync,
nameof(IRemoteDependentTypeFinder.FindAndCacheDerivedInterfacesAsync),
cancellationToken).ConfigureAwait(false);
if (result.HasValue)
return result.Value;
return await FindAndCacheDerivedInterfacesInCurrentProcessAsync(
type, solution, projects, transitive, cancellationToken).ConfigureAwait(false);
}
private static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheDerivedInterfacesInCurrentProcessAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
......
......@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
namespace Microsoft.CodeAnalysis.FindSymbols
{
......@@ -13,11 +14,27 @@ namespace Microsoft.CodeAnalysis.FindSymbols
internal static partial class DependentTypeFinder
{
/// <summary>
/// Implementation of <see cref="SymbolFinder.FindImplementationsAsync(ISymbol, Solution, IImmutableSet{Project}, CancellationToken)"/> for
/// <see cref="INamedTypeSymbol"/>s
/// </summary>
public static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheImplementingTypesAsync(
public static async Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheImplementingTypesAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
bool transitive,
CancellationToken cancellationToken)
{
var result = await TryFindAndCacheRemoteTypesAsync(
type, solution, projects, transitive,
FunctionId.DependentTypeFinder_FindAndCacheImplementingTypesAsync,
nameof(IRemoteDependentTypeFinder.FindAndCacheImplementingTypesAsync),
cancellationToken).ConfigureAwait(false);
if (result.HasValue)
return result.Value;
return await FindAndCacheImplementingTypesInCurrentProcessAsync(
type, solution, projects, transitive, cancellationToken).ConfigureAwait(false);
}
private static Task<ImmutableArray<INamedTypeSymbol>> FindAndCacheImplementingTypesInCurrentProcessAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
......@@ -67,9 +84,14 @@ static bool TypeMatches(INamedTypeSymbol type, SymbolSet set)
transitive: transitive,
cancellationToken: cancellationToken).ConfigureAwait(false);
// Only classes/struct implement interface types. Derived interfaces can be found with
// FindDerivedInterfacesAsync.
return allTypes.WhereAsArray(t => t.TypeKind == TypeKind.Class || t.TypeKind == TypeKind.Struct);
// Only classes/struct/delegates/enums implement interface types. Derived interfaces can be found with
// FindDerivedInterfacesAsync. Delegates/Enums only happen in a few corner cases. For example, enums
// implement IComparable, and delegates implement ICloneable.
return allTypes.WhereAsArray(
t => t.TypeKind == TypeKind.Class ||
t.TypeKind == TypeKind.Struct ||
t.TypeKind == TypeKind.Delegate ||
t.TypeKind == TypeKind.Enum);
}
return ImmutableArray<INamedTypeSymbol>.Empty;
......
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Remote;
namespace Microsoft.CodeAnalysis.FindSymbols
{
internal static partial class DependentTypeFinder
{
public static async Task<ImmutableArray<INamedTypeSymbol>?> TryFindAndCacheRemoteTypesAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
bool transitive,
FunctionId functionId,
string remoteFunctionName,
CancellationToken cancellationToken)
{
using (Logger.LogBlock(functionId, cancellationToken))
{
var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false);
if (client != null)
{
var result = await client.TryRunRemoteAsync<ImmutableArray<SerializableSymbolAndProjectId>>(
WellKnownServiceHubServices.CodeAnalysisService,
remoteFunctionName,
solution,
new object[]
{
SerializableSymbolAndProjectId.Dehydrate(solution, type, cancellationToken),
projects?.Select(p => p.Id).ToArray(),
transitive,
},
null,
cancellationToken).ConfigureAwait(false);
if (result.HasValue)
{
return await RehydrateAsync(solution, result.Value, cancellationToken).ConfigureAwait(false);
}
}
}
return null;
}
private static async Task<ImmutableArray<INamedTypeSymbol>> RehydrateAsync(Solution solution, ImmutableArray<SerializableSymbolAndProjectId> values, CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<INamedTypeSymbol>.GetInstance(out var builder);
foreach (var item in values)
{
var rehydrated = await item.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false);
if (rehydrated is INamedTypeSymbol namedType)
builder.AddIfNotNull(namedType);
}
return builder.ToImmutable();
}
}
}
// 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.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
namespace Microsoft.CodeAnalysis.FindSymbols
{
internal interface IRemoteDependentTypeFinder
{
Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheDerivedClassesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId type,
ProjectId[] projects,
bool transitive,
CancellationToken cancellationToken);
Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheDerivedInterfacesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId type,
ProjectId[] projects,
bool transitive,
CancellationToken cancellationToken);
Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheImplementingTypesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId type,
ProjectId[] projects,
bool transitive,
CancellationToken cancellationToken);
}
}
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
namespace Microsoft.CodeAnalysis.Remote
{
// root level service for all Roslyn services
internal partial class CodeAnalysisService : IRemoteDependentTypeFinder
{
private Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheTypesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId typeAndProjectId,
ProjectId[] projectIds,
Func<INamedTypeSymbol, Solution, ImmutableHashSet<Project>, Task<ImmutableArray<INamedTypeSymbol>>> func,
CancellationToken cancellationToken)
{
return RunServiceAsync(async () =>
{
using (UserOperationBooster.Boost())
{
var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false);
var symbol = await typeAndProjectId.TryRehydrateAsync(
solution, cancellationToken).ConfigureAwait(false);
if (!(symbol is INamedTypeSymbol namedType))
return ImmutableArray<SerializableSymbolAndProjectId>.Empty;
var projects = projectIds?.Select(id => solution.GetProject(id)).ToImmutableHashSet();
var types = await func(namedType, solution, projects).ConfigureAwait(false);
return types.SelectAsArray(
(Func<INamedTypeSymbol, SerializableSymbolAndProjectId>)(t => SerializableSymbolAndProjectId.Dehydrate(solution, t, cancellationToken)));
}
}, cancellationToken);
}
public Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheDerivedClassesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId typeAndProjectId,
ProjectId[] projectIds,
bool transitive,
CancellationToken cancellationToken)
{
return FindAndCacheTypesAsync(
solutionInfo, typeAndProjectId, projectIds,
(nt, s, ps) => DependentTypeFinder.FindAndCacheDerivedClassesAsync(nt, s, ps, transitive, cancellationToken),
cancellationToken);
}
public Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheDerivedInterfacesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId typeAndProjectId,
ProjectId[] projectIds,
bool transitive,
CancellationToken cancellationToken)
{
return FindAndCacheTypesAsync(
solutionInfo, typeAndProjectId, projectIds,
(nt, s, ps) => DependentTypeFinder.FindAndCacheDerivedInterfacesAsync(nt, s, ps, transitive, cancellationToken),
cancellationToken);
}
public Task<ImmutableArray<SerializableSymbolAndProjectId>> FindAndCacheImplementingTypesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId typeAndProjectId,
ProjectId[] projectIds,
bool transitive,
CancellationToken cancellationToken)
{
return FindAndCacheTypesAsync(
solutionInfo, typeAndProjectId, projectIds,
(nt, s, ps) => DependentTypeFinder.FindAndCacheImplementingTypesAsync(nt, s, ps, transitive, cancellationToken),
cancellationToken);
}
}
}
......@@ -484,10 +484,14 @@ internal enum FunctionId
Renamer_FindRenameLocationsAsync = 387,
Renamer_ResolveConflictsAsync = 388,
ChangeSignature_Data = 389,
ChangeSignature_Data = 400,
AbstractEncapsulateFieldService_EncapsulateFieldsAsync = 390,
AbstractEncapsulateFieldService_EncapsulateFieldsAsync = 410,
AbstractConvertTupleToStructCodeRefactoringProvider_ConvertToStructAsync = 400,
AbstractConvertTupleToStructCodeRefactoringProvider_ConvertToStructAsync = 420,
DependentTypeFinder_FindAndCacheDerivedClassesAsync = 430,
DependentTypeFinder_FindAndCacheDerivedInterfacesAsync = 431,
DependentTypeFinder_FindAndCacheImplementingTypesAsync = 432,
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册