private async Task FindResultsInUnreferencedMetadataSymbolsAsync( ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { if (allSymbolReferences.Count > 0) { // Only do this if none of the project searches produced any results. We may have // a lot of metadata to search through, and it would be good to avoid that if we // can. return; } // 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 <PortableExecutableReference>(comparer: this); seenReferences.AddAll(project.MetadataReferences.OfType <PortableExecutableReference>()); var newReferences = project.Solution.Projects.Where(p => p != project) .SelectMany(p => p.MetadataReferences.OfType <PortableExecutableReference>()) .Distinct(comparer: this) .Where(r => !seenReferences.Contains(r)) .Where(r => !IsInPackagesDirectory(r)); // Search all metadata references in parallel. var findTasks = new HashSet <Task <List <SymbolReference> > >(); // Create another cancellation token so we can both search all projects in parallel, // but also stop any searches once we get enough results. using (var nestedTokenSource = new CancellationTokenSource()) using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken)) { foreach (var reference in newReferences) { 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. var assembly = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol; if (assembly != null) { findTasks.Add(finder.FindInMetadataAsync(project.Solution, assembly, reference, exact, linkedTokenSource.Token)); } } await WaitForTasksAsync(allSymbolReferences, findTasks, nestedTokenSource, cancellationToken).ConfigureAwait(false); } }
private async Task FindResultsInMetadataReferences( Project otherProject, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, HashSet <PortableExecutableReference> seenReferences, bool exact, CancellationToken cancellationToken) { // See if this project has a metadata reference we haven't already looked at. var newMetadataReferences = otherProject.MetadataReferences.OfType <PortableExecutableReference>(); Compilation compilation = null; foreach (var reference in newMetadataReferences) { // Make sure we don't check the same metadata reference multiple times from // different projects. if (seenReferences.Add(reference)) { // Defer making the compilation until necessary. compilation = compilation ?? await otherProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); // Ignore netmodules. First, they're incredibly esoteric and barely used. // Second, the SymbolFinder api doesn't even support searching them. var assembly = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol; if (assembly != null) { AddRange(allSymbolReferences, await finder.FindInMetadataAsync(otherProject.Solution, assembly, reference, exact).ConfigureAwait(false)); } } if (allSymbolReferences.Count >= MaxResults) { break; } } }
private async Task FindResultsInUnreferencedMetadataSymbolsAsync( ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { if (allSymbolReferences.Count > 0) { // Only do this if none of the project searches produced any results. We may have // a lot of metadata to search through, and it would be good to avoid that if we // can. return; } // 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 <PortableExecutableReference>(comparer: this); seenReferences.AddAll(project.MetadataReferences.OfType <PortableExecutableReference>()); var newReferences = project.Solution.Projects.Where(p => p != project) .SelectMany(p => p.MetadataReferences.OfType <PortableExecutableReference>()) .Distinct(comparer: this) .Where(r => !seenReferences.Contains(r)); // Search all metadata references in parallel. var findTasks = new HashSet <Task <List <SymbolReference> > >(); foreach (var reference in newReferences) { 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. var assembly = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol; if (assembly != null) { findTasks.Add(finder.FindInMetadataAsync(project.Solution, assembly, reference, exact)); } } while (findTasks.Count > 0) { // Keep on looping through the 'find' tasks, processing each when they finish. cancellationToken.ThrowIfCancellationRequested(); var doneTask = await Task.WhenAny(findTasks).ConfigureAwait(false); // One of the tasks finished. Remove it from the list we're waiting on. findTasks.Remove(doneTask); // Add its results to the final result set we're keeping. AddRange(allSymbolReferences, await doneTask.ConfigureAwait(false)); // If we've got enough, no need to keep searching. // Note: We do not cancel the existing tasks that are still executing. These tasks will // cause our indices to be created if necessary. And that's good for future searches which // we will invariably perform. if (allSymbolReferences.Count >= MaxResults) { break; } } }