diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs index 26a90a1d00b19..a8e0b7d21008b 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs @@ -18,7 +18,8 @@ namespace Microsoft.CodeAnalysis.CodeFixes.AddImport { - internal abstract partial class AbstractAddImportCodeFixProvider : CodeFixProvider, IEqualityComparer + internal abstract partial class AbstractAddImportCodeFixProvider + : CodeFixProvider, IEqualityComparer<(ProjectId, PortableExecutableReference)> where TSimpleNameSyntax : SyntaxNode { private const int MaxResults = 3; @@ -249,15 +250,10 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( // Keep track of the references we've seen (so that we don't process them multiple times // across many sibling projects). Prepopulate it with our own metadata references since // we know we don't need to search in that. - var seenReferences = new HashSet(comparer: this); - seenReferences.AddAll(project.MetadataReferences.OfType()); + var seenReferences = new HashSet<(ProjectId, PortableExecutableReference)>(comparer: this); + seenReferences.AddAll(project.MetadataReferences.OfType().Select(r => (project.Id, r))); - var newReferences = - project.Solution.Projects.Where(p => p != project) - .SelectMany(p => p.MetadataReferences.OfType()) - .Distinct(comparer: this) - .Where(r => !seenReferences.Contains(r)) - .Where(r => !IsInPackagesDirectory(r)); + var newReferences = GetUnreferencedMetadataReferences(project, seenReferences); // Search all metadata references in parallel. var findTasks = new HashSet>>(); @@ -267,9 +263,10 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( using (var nestedTokenSource = new CancellationTokenSource()) using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken)) { - foreach (var reference in newReferences) + foreach (var (referenceProjectId, reference) in newReferences) { - var compilation = referenceToCompilation.GetOrAdd(reference, r => CreateCompilation(project, r)); + var compilation = referenceToCompilation.GetOrAdd( + reference, r => CreateCompilation(project, r)); // Ignore netmodules. First, they're incredibly esoteric and barely used. // Second, the SymbolFinder API doesn't even support searching them. @@ -277,7 +274,7 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( if (assembly != null) { findTasks.Add(finder.FindInMetadataSymbolsAsync( - assembly, reference, exact, linkedTokenSource.Token)); + assembly, referenceProjectId, reference, exact, linkedTokenSource.Token)); } } @@ -285,6 +282,23 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( } } + /// + /// Returns the set of PEReferences in the solution that are not currently being referenced + /// by this project. The set returned will be tuples containing the PEReference, and the project-id + /// for the project we found the pe-reference in. + /// + private ImmutableArray<(ProjectId, PortableExecutableReference)> GetUnreferencedMetadataReferences( + Project project, HashSet<(ProjectId, PortableExecutableReference)> seenReferences) + { + var solution = project.Solution; + return solution.Projects.Where(p => p != project) + .SelectMany(p => p.MetadataReferences.OfType().Select(pe => (p.Id, reference: pe))) + .Distinct(comparer: this) + .Where(t => !seenReferences.Contains(t)) + .Where(t => !IsInPackagesDirectory(t.Item2)) + .ToImmutableArray(); + } + private async Task WaitForTasksAsync( ArrayBuilder allSymbolReferences, HashSet>> findTasks, @@ -364,16 +378,17 @@ private Compilation CreateCompilation(Project project, PortableExecutableReferen } - bool IEqualityComparer.Equals(PortableExecutableReference x, PortableExecutableReference y) + bool IEqualityComparer<(ProjectId, PortableExecutableReference)>.Equals( + (ProjectId, PortableExecutableReference) x, (ProjectId, PortableExecutableReference) y) { return StringComparer.OrdinalIgnoreCase.Equals( - x.FilePath ?? x.Display, - y.FilePath ?? y.Display); + x.Item2.FilePath ?? x.Item2.Display, + y.Item2.FilePath ?? y.Item2.Display); } - int IEqualityComparer.GetHashCode(PortableExecutableReference obj) + int IEqualityComparer<(ProjectId, PortableExecutableReference)>.GetHashCode((ProjectId, PortableExecutableReference) obj) { - return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.FilePath ?? obj.Display); + return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Item2.FilePath ?? obj.Item2.Display); } private static HashSet GetViableUnreferencedProjects(Project project) diff --git a/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.InstallPackageAndAddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.InstallPackageAndAddImportCodeAction.cs index 0c6af8bdf7d4d..3052360f019dc 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.InstallPackageAndAddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.InstallPackageAndAddImportCodeAction.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.AddImport { - internal abstract partial class AbstractAddImportCodeFixProvider : CodeFixProvider, IEqualityComparer + internal abstract partial class AbstractAddImportCodeFixProvider { private partial class PackageReference { diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs index 34513044ad5b6..23e9c08b18204 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs @@ -25,10 +25,13 @@ public AllSymbolsProjectSearchScope( { } - protected override Task> FindDeclarationsAsync( + protected override async Task> FindDeclarationsAsync( string name, SymbolFilter filter, SearchQuery searchQuery) { - return SymbolFinder.FindAllDeclarationsWithNormalQueryAsync(_project, searchQuery, filter, CancellationToken); + var declarations = await SymbolFinder.FindAllDeclarationsWithNormalQueryAsync( + _project, searchQuery, filter, CancellationToken).ConfigureAwait(false); + + return declarations.SelectAsArray(d => d.Symbol); } } } diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs index 87f1a2cccd577..9117b5c4b1eb8 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs @@ -12,14 +12,16 @@ internal abstract partial class AbstractAddImportCodeFixProvider provider, Solution solution, IAssemblySymbol assembly, + ProjectId assemblyProjectId, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) @@ -27,6 +29,7 @@ public MetadataSymbolsSearchScope( { _solution = solution; _assembly = assembly; + _assemblyProjectId = assemblyProjectId; _metadataReference = metadataReference; } @@ -48,7 +51,11 @@ protected override async Task> FindDeclarationsAsync( return ImmutableArray.Empty; } - return await info.FindAsync(searchQuery, _assembly, filter, CancellationToken).ConfigureAwait(false); + var declarations = await info.FindAsync( + searchQuery, _assembly, _assemblyProjectId, + filter, CancellationToken).ConfigureAwait(false); + + return declarations.SelectAsArray(d => d.Symbol); } } } diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs index 53b08863b77bf..d3e439e233a3d 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs @@ -45,7 +45,11 @@ protected override async Task> FindDeclarationsAsync( // needed. var lazyAssembly = _projectToAssembly.GetOrAdd(_project, CreateLazyAssembly); - return await info.FindAsync(searchQuery, lazyAssembly, filter, CancellationToken).ConfigureAwait(false); + var declarations = await info.FindAsync( + searchQuery, lazyAssembly, _project.Id, + filter, CancellationToken).ConfigureAwait(false); + + return declarations.SelectAsArray(d => d.Symbol); } private static AsyncLazy CreateLazyAssembly(Project project) diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 210094f4ce622..2b2fbaa856b06 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs @@ -89,11 +89,12 @@ internal Task> FindInSourceSymbolsInProjectAsync } internal Task> FindInMetadataSymbolsAsync( - IAssemblySymbol assembly, PortableExecutableReference metadataReference, + IAssemblySymbol assembly, ProjectId assemblyProjectId, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) { var searchScope = new MetadataSymbolsSearchScope( - _owner, _document.Project.Solution, assembly, metadataReference, exact, cancellationToken); + _owner, _document.Project.Solution, assembly, assemblyProjectId, + metadataReference, exact, cancellationToken); return DoAsync(searchScope); } diff --git a/src/Features/Core/Portable/CodeFixes/Qualify/AbstractFullyQualifyCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Qualify/AbstractFullyQualifyCodeFixProvider.cs index b72d316881521..f2cd4f3edb03b 100644 --- a/src/Features/Core/Portable/CodeFixes/Qualify/AbstractFullyQualifyCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Qualify/AbstractFullyQualifyCodeFixProvider.cs @@ -140,14 +140,17 @@ private async Task> GetMatchingTypesAsync( var syntaxFacts = project.LanguageServices.GetService(); syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity); - var symbols = await SymbolFinder.FindDeclarationsAsync(project, name, this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); + var symbolAndProjectIds = await SymbolFinder.FindAllDeclarationsAsync( + project, name, this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); + var symbols = symbolAndProjectIds.SelectAsArray(t => t.Symbol); // also lookup type symbols with the "Attribute" suffix. var inAttributeContext = syntaxFacts.IsAttributeName(node); if (inAttributeContext) { - symbols = symbols.Concat( - await SymbolFinder.FindDeclarationsAsync(project, name + "Attribute", this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false)); + var attributeSymbolAndProjectIds = await SymbolFinder.FindAllDeclarationsAsync( + project, name + "Attribute", this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); + symbols = symbols.Concat(attributeSymbolAndProjectIds.SelectAsArray(t => t.Symbol)); } var accessibleTypeSymbols = symbols @@ -186,9 +189,11 @@ private async Task> GetMatchingNamespacesAsync( return ImmutableArray.Empty; } - var symbols = await SymbolFinder.FindDeclarationsAsync( + var symbolAndProjectIds = await SymbolFinder.FindAllDeclarationsAsync( project, name, this.IgnoreCase, SymbolFilter.Namespace, cancellationToken).ConfigureAwait(false); + var symbols = symbolAndProjectIds.SelectAsArray(t => t.Symbol); + // There might be multiple namespaces that this name will resolve successfully in. // Some of them may be 'better' results than others. For example, say you have // Y.Z and Y exists in both X1 and X2 diff --git a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb index bf8e493d39f85..46376c62541c5 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEvent/GenerateEventCodeFixProvider.vb @@ -107,9 +107,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent Dim syntaxFactService = document.Project.Solution.Workspace.Services.GetLanguageServices(targetType.Language).GetService(Of ISyntaxFactsService) Dim eventHandlerName As String = actualEventName + "Handler" - Dim existingSymbols = Await SymbolFinder.FindSourceDeclarationsAsync( + Dim existingSymbolAndProjectIds = Await SymbolFinder.FindSourceDeclarationsWithNormalQueryInLocalProcessAsync( document.Project.Solution, eventHandlerName, Not syntaxFactService.IsCaseSensitive, SymbolFilter.Type, cancellationToken).ConfigureAwait(False) + Dim existingSymbols = existingSymbolAndProjectIds.SelectAsArray(Function(t) t.Symbol) If existingSymbols.Any(Function(existingSymbol) existingSymbol IsNot Nothing _ AndAlso existingSymbol.ContainingNamespace Is targetType.ContainingNamespace) Then ' There already exists a delegate that matches the event handler name diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations.cs index 7a6fc03717a40..a28fad59bd3fa 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { @@ -15,7 +14,7 @@ public static partial class SymbolFinder { private static Task AddCompilationDeclarationsWithNormalQueryAsync( Project project, SearchQuery query, SymbolFilter filter, - ArrayBuilder list, CancellationToken cancellationToken) + ArrayBuilder list, CancellationToken cancellationToken) { Debug.Assert(query.Kind != SearchKind.Custom); return AddCompilationDeclarationsWithNormalQueryAsync( @@ -29,7 +28,7 @@ private static async Task AddCompilationDeclarationsWithNormalQueryAsync( Project project, SearchQuery query, SymbolFilter filter, - ArrayBuilder list, + ArrayBuilder list, Compilation startingCompilation, IAssemblySymbol startingAssembly, CancellationToken cancellationToken) @@ -45,14 +44,15 @@ private static async Task AddCompilationDeclarationsWithNormalQueryAsync( var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var symbolsWithName = compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken) + .Select(s => new SymbolAndProjectId(s, project.Id)) .ToImmutableArray(); if (startingCompilation != null && startingAssembly != null && compilation.Assembly != startingAssembly) { // Return symbols from skeleton assembly in this case so that symbols have // the same language as startingCompilation. - symbolsWithName = symbolsWithName.Select(s => s.GetSymbolKey().Resolve(startingCompilation, cancellationToken: cancellationToken).Symbol) - .WhereNotNull() + symbolsWithName = symbolsWithName.Select(s => s.WithSymbol(s.Symbol.GetSymbolKey().Resolve(startingCompilation, cancellationToken: cancellationToken).Symbol)) + .Where(s => s.Symbol != null) .ToImmutableArray(); } @@ -61,8 +61,9 @@ private static async Task AddCompilationDeclarationsWithNormalQueryAsync( } private static async Task AddMetadataDeclarationsWithNormalQueryAsync( - Solution solution, IAssemblySymbol assembly, PortableExecutableReference referenceOpt, - SearchQuery query, SymbolFilter filter, ArrayBuilder list, CancellationToken cancellationToken) + Project project, IAssemblySymbol assembly, PortableExecutableReference referenceOpt, + SearchQuery query, SymbolFilter filter, ArrayBuilder list, + CancellationToken cancellationToken) { // All entrypoints to this function are Find functions that are only searching // for specific strings (i.e. they never do a custom search). @@ -73,18 +74,19 @@ private static async Task AddMetadataDeclarationsWithNormalQueryAsync( if (referenceOpt != null) { var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( - solution, referenceOpt, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + project.Solution, referenceOpt, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); if (info != null) { - var symbols = await info.FindAsync(query, assembly, filter, cancellationToken).ConfigureAwait(false); + var symbols = await info.FindAsync( + query, assembly, project.Id, filter, cancellationToken).ConfigureAwait(false); list.AddRange(symbols); } } } } - internal static ImmutableArray FilterByCriteria(ImmutableArray symbols, SymbolFilter criteria) - => symbols.WhereAsArray(s => MeetCriteria(s, criteria)); + internal static ImmutableArray FilterByCriteria(ImmutableArray symbols, SymbolFilter criteria) + => symbols.WhereAsArray(s => MeetCriteria(s.Symbol, criteria)); private static bool MeetCriteria(ISymbol symbol, SymbolFilter filter) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_AllDeclarations.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_AllDeclarations.cs index 1f3ed6bcec2b9..2b87e25469c06 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_AllDeclarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_AllDeclarations.cs @@ -17,11 +17,46 @@ namespace Microsoft.CodeAnalysis.FindSymbols public static partial class SymbolFinder { + #region Legacy API + + // This region contains the legacy FindDeclarations APIs. The APIs are legacy because they + // do not contain enough information for us to effectively remote them over to the OOP + // process to do the work. Specifically, they lack the "current project context" necessary + // to be able to effectively serialize symbols to/from the remote process. + /// /// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name. /// public static async Task> FindDeclarationsAsync( Project project, string name, bool ignoreCase, CancellationToken cancellationToken = default(CancellationToken)) + { + var declarations = await FindAllDeclarationsAsync(project, name, ignoreCase, cancellationToken).ConfigureAwait(false); + return declarations.SelectAsArray(t => t.Symbol); + } + + /// + /// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name. + /// + public static async Task> FindDeclarationsAsync( + Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken)) + { + var declarations = await FindAllDeclarationsAsync(project, name, ignoreCase, filter, cancellationToken).ConfigureAwait(false); + return declarations.SelectAsArray(t => t.Symbol); + } + + #endregion + + #region Current API + + // This region contains the current FindDeclaratins APIs. The current APIs allow for OOP + // implementation and will defer to the oop server if it is available. If not, it will + // compute the results in process. + + /// + /// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name. + /// + internal static async Task> FindAllDeclarationsAsync( + Project project, string name, bool ignoreCase, CancellationToken cancellationToken) { if (name == null) { @@ -30,7 +65,7 @@ public static async Task> FindDeclarationsAsync( if (string.IsNullOrWhiteSpace(name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } return await FindAllDeclarationsWithNormalQueryAsync( @@ -40,7 +75,7 @@ public static async Task> FindDeclarationsAsync( /// /// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name. /// - public static async Task> FindDeclarationsAsync( + internal static async Task> FindAllDeclarationsAsync( Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken)) { if (name == null) @@ -50,14 +85,14 @@ public static async Task> FindDeclarationsAsync( if (string.IsNullOrWhiteSpace(name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } return await FindAllDeclarationsWithNormalQueryAsync( project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken: cancellationToken).ConfigureAwait(false); } - internal static async Task> FindAllDeclarationsWithNormalQueryAsync( + internal static async Task> FindAllDeclarationsWithNormalQueryAsync( Project project, SearchQuery query, SymbolFilter criteria, CancellationToken cancellationToken) { // All entrypoints to this function are Find functions that are only searching @@ -71,15 +106,16 @@ internal static async Task> FindAllDeclarationsWithNorma if (query.Name != null && string.IsNullOrWhiteSpace(query.Name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var list = ArrayBuilder.GetInstance(); + var list = ArrayBuilder.GetInstance(); // get declarations from the compilation's assembly - await AddCompilationDeclarationsWithNormalQueryAsync(project, query, criteria, list, cancellationToken).ConfigureAwait(false); + await AddCompilationDeclarationsWithNormalQueryAsync( + project, query, criteria, list, cancellationToken).ConfigureAwait(false); // get declarations from directly referenced projects and metadata foreach (var assembly in compilation.GetReferencedAssemblySymbols()) @@ -87,27 +123,34 @@ internal static async Task> FindAllDeclarationsWithNorma var assemblyProject = project.Solution.GetProject(assembly, cancellationToken); if (assemblyProject != null) { - await AddCompilationDeclarationsWithNormalQueryAsync(assemblyProject, query, criteria, list, compilation, assembly, cancellationToken).ConfigureAwait(false); + await AddCompilationDeclarationsWithNormalQueryAsync( + assemblyProject, query, criteria, list, + compilation, assembly, cancellationToken).ConfigureAwait(false); } else { await AddMetadataDeclarationsWithNormalQueryAsync( - project.Solution, assembly, compilation.GetMetadataReference(assembly) as PortableExecutableReference, + project, assembly, compilation.GetMetadataReference(assembly) as PortableExecutableReference, query, criteria, list, cancellationToken).ConfigureAwait(false); } } - // Make certain all namespace symbols returned by API are from the compilation. + // Make certain all namespace symbols returned by API are from the compilation + // for the passed in project. for (var i = 0; i < list.Count; i++) { - var symbol = list[i]; - if (symbol is INamespaceSymbol ns) + var symbolAndProjectId = list[i]; + if (symbolAndProjectId.Symbol is INamespaceSymbol ns) { - list[i] = compilation.GetCompilationNamespace(ns); + list[i] = new SymbolAndProjectId( + compilation.GetCompilationNamespace(ns), + project.Id); } } return list.ToImmutableAndFree(); } + + #endregion } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_CustomQueries.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_CustomQueries.cs index 75687008f46b6..b3ed1990d7631 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_CustomQueries.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_CustomQueries.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; @@ -28,10 +29,14 @@ public static partial class SymbolFinder /// Find the symbols for declarations made in source with a matching name. /// public static async Task> FindSourceDeclarationsAsync(Solution solution, Func predicate, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken)) - => await FindSourceDeclarationsWithCustomQueryAsync( + { + var declarations = await FindSourceDeclarationsWithCustomQueryAsync( solution, SearchQuery.CreateCustom(predicate), filter, cancellationToken).ConfigureAwait(false); - private static async Task> FindSourceDeclarationsWithCustomQueryAsync( + return declarations.SelectAsArray(d => d.Symbol); + } + + private static async Task> FindSourceDeclarationsWithCustomQueryAsync( Solution solution, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken) { if (solution == null) @@ -41,12 +46,12 @@ private static async Task> FindSourceDeclarationsWithCus if (query.Name != null && string.IsNullOrWhiteSpace(query.Name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } using (Logger.LogBlock(FunctionId.SymbolFinder_Solution_Predicate_FindSourceDeclarationsAsync, cancellationToken)) { - var result = ArrayBuilder.GetInstance(); + var result = ArrayBuilder.GetInstance(); foreach (var projectId in solution.ProjectIds) { var project = solution.GetProject(projectId); @@ -68,10 +73,14 @@ private static async Task> FindSourceDeclarationsWithCus /// Find the symbols for declarations made in source with a matching name. /// public static async Task> FindSourceDeclarationsAsync(Project project, Func predicate, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken)) - => await FindSourceDeclarationsWithCustomQueryAsync( + { + var declarations = await FindSourceDeclarationsWithCustomQueryAsync( project, SearchQuery.CreateCustom(predicate), filter, cancellationToken).ConfigureAwait(false); - private static async Task> FindSourceDeclarationsWithCustomQueryAsync( + return declarations.SelectAsArray(d => d.Symbol); + } + + private static async Task> FindSourceDeclarationsWithCustomQueryAsync( Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken) { if (project == null) @@ -81,19 +90,22 @@ private static async Task> FindSourceDeclarationsWithCus if (query.Name != null && string.IsNullOrWhiteSpace(query.Name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } using (Logger.LogBlock(FunctionId.SymbolFinder_Project_Predicate_FindSourceDeclarationsAsync, cancellationToken)) { if (!await project.ContainsSymbolsWithNameAsync(query.GetPredicate(), filter, cancellationToken).ConfigureAwait(false)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var unfiltered = compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken).ToImmutableArray(); + var unfiltered = compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken) + .Select(s => new SymbolAndProjectId(s, project.Id)) + .ToImmutableArray(); + return FilterByCriteria(unfiltered, filter); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_SourceDeclarations.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_SourceDeclarations.cs index e707faabfa8e6..82b73a3da6403 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_SourceDeclarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations_SourceDeclarations.cs @@ -16,6 +16,13 @@ namespace Microsoft.CodeAnalysis.FindSymbols public static partial class SymbolFinder { + #region Legacy API + + // This region contains the legacy FindDeclarations APIs. The APIs are legacy because they + // do not contain enough information for us to effectively remote them over to the OOP + // process to do the work. Specifically, they lack the "current project context" necessary + // to be able to effectively serialize symbols to/from the remote process. + /// /// Find the symbols for declarations made in source with the specified name. /// @@ -30,8 +37,9 @@ public static async Task> FindSourceDeclarationsAsync( { using (Logger.LogBlock(FunctionId.SymbolFinder_Solution_Name_FindSourceDeclarationsAsync, cancellationToken)) { - return await FindSourceDeclarationsWithNormalQueryAsync( + var declarations = await FindSourceDeclarationsWithNormalQueryInLocalProcessAsync( solution, name, ignoreCase, filter, cancellationToken).ConfigureAwait(false); + return declarations.SelectAsArray(t => t.Symbol); } } @@ -49,12 +57,22 @@ public static async Task> FindSourceDeclarationsAsync( { using (Logger.LogBlock(FunctionId.SymbolFinder_Project_Name_FindSourceDeclarationsAsync, cancellationToken)) { - return await FindSourceDeclarationsithNormalQueryInLocalProcessAsync( + var declarations = await FindSourceDeclarationsithNormalQueryInLocalProcessAsync( project, name, ignoreCase, filter, cancellationToken).ConfigureAwait(false); + + return declarations.SelectAsArray(t => t.Symbol); } } - private static async Task> FindSourceDeclarationsWithNormalQueryAsync( + #endregion + + #region Current API + + // This region contains the current FindDeclaratins APIs. The current APIs allow for OOP + // implementation and will defer to the oop server if it is available. If not, it will + // compute the results in process. + + internal static async Task> FindSourceDeclarationsWithNormalQueryInLocalProcessAsync( Solution solution, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken) { if (solution == null) @@ -69,11 +87,11 @@ private static async Task> FindSourceDeclarationsWithNor if (string.IsNullOrWhiteSpace(name)) { - return ImmutableArray.Empty; + return ImmutableArray.Empty; } var query = SearchQuery.Create(name, ignoreCase); - var result = ArrayBuilder.GetInstance(); + var result = ArrayBuilder.GetInstance(); foreach (var projectId in solution.ProjectIds) { var project = solution.GetProject(projectId); @@ -84,7 +102,7 @@ await AddCompilationDeclarationsWithNormalQueryAsync( return result.ToImmutableAndFree(); } - private static async Task> FindSourceDeclarationsithNormalQueryInLocalProcessAsync( + private static async Task> FindSourceDeclarationsithNormalQueryInLocalProcessAsync( Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken) { if (project == null) @@ -99,13 +117,16 @@ private static async Task> FindSourceDeclarationsithNormalQ if (string.IsNullOrWhiteSpace(name)) { - return SpecializedCollections.EmptyEnumerable(); + return ImmutableArray.Empty; } - var list = ArrayBuilder.GetInstance(); + var list = ArrayBuilder.GetInstance(); await AddCompilationDeclarationsWithNormalQueryAsync( - project, SearchQuery.Create(name, ignoreCase), filter, list, cancellationToken).ConfigureAwait(false); + project, SearchQuery.Create(name, ignoreCase), + filter, list, cancellationToken).ConfigureAwait(false); return list.ToImmutableAndFree(); } + + #endregion } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 727ed764c2f01..74567a37b6954 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -114,25 +114,30 @@ private SymbolTreeInfo( _spellCheckerTask = spellCheckerTask; } - public Task> FindAsync( - SearchQuery query, IAssemblySymbol assembly, SymbolFilter filter, CancellationToken cancellationToken) + public Task> FindAsync( + SearchQuery query, IAssemblySymbol assembly, ProjectId assemblyProjectId, SymbolFilter filter, CancellationToken cancellationToken) { // All entrypoints to this function are Find functions that are only searching // for specific strings (i.e. they never do a custom search). Debug.Assert(query.Kind != SearchKind.Custom); - return this.FindAsync(query, new AsyncLazy(assembly), filter, cancellationToken); + return this.FindAsync( + query, new AsyncLazy(assembly), + assemblyProjectId, filter, cancellationToken); } - public async Task> FindAsync( - SearchQuery query, AsyncLazy lazyAssembly, SymbolFilter filter, CancellationToken cancellationToken) + public async Task> FindAsync( + SearchQuery query, AsyncLazy lazyAssembly, ProjectId assemblyProjectId, + SymbolFilter filter, CancellationToken cancellationToken) { // All entrypoints to this function are Find functions that are only searching // for specific strings (i.e. they never do a custom search). Debug.Assert(query.Kind != SearchKind.Custom); + var symbols = await FindAsyncWorker(query, lazyAssembly, cancellationToken).ConfigureAwait(false); + return SymbolFinder.FilterByCriteria( - await FindAsyncWorker(query, lazyAssembly, cancellationToken).ConfigureAwait(false), + symbols.SelectAsArray(s => new SymbolAndProjectId(s, assemblyProjectId)), filter); }