private async Task FindResultsInAllProjectSymbolsAsync( Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact) { var references = await finder.FindInAllProjectSymbolsAsync(project, exact).ConfigureAwait(false); AddRange(allSymbolReferences, references); }
private async Task <ImmutableArray <Reference> > FindResultsAsync( ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> > projectToAssembly, ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { var allReferences = ArrayBuilder <Reference> .GetInstance(); // First search the current project to see if any symbols (source or metadata) match the // search string. await FindResultsInAllSymbolsInStartingProjectAsync( allReferences, maxResults, finder, exact, cancellationToken).ConfigureAwait(false); // Only bother doing this for host workspaces. We don't want this for // things like the Interactive workspace as we can't even add project // references to the interactive window. We could consider adding metadata // references with #r in the future. if (IsHostOrTestOrRemoteWorkspace(project)) { // Now search unreferenced projects, and see if they have any source symbols that match // the search string. await FindResultsInUnreferencedProjectSourceSymbolsAsync(projectToAssembly, project, allReferences, maxResults, finder, exact, cancellationToken).ConfigureAwait(false); // Finally, check and see if we have any metadata symbols that match the search string. await FindResultsInUnreferencedMetadataSymbolsAsync(referenceToCompilation, project, allReferences, maxResults, finder, exact, cancellationToken).ConfigureAwait(false); // We only support searching NuGet in an exact manner currently. if (exact) { await finder.FindNugetOrReferenceAssemblyReferencesAsync(allReferences, cancellationToken).ConfigureAwait(false); } } return(allReferences.ToImmutableAndFree()); }
private async Task <ImmutableArray <Reference> > FindResultsAsync( Document document, SemanticModel semanticModel, Diagnostic diagnostic, SyntaxNode node, CancellationToken cancellationToken) { // Caches so we don't produce the same data multiple times while searching // all over the solution. var project = document.Project; var projectToAssembly = new ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> >(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary <PortableExecutableReference, Compilation>(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnostic, node, cancellationToken); // Look for exact matches first: var exactReferences = await FindResultsAsync(projectToAssembly, referenceToCompilation, project, finder, exact : true, cancellationToken : cancellationToken).ConfigureAwait(false); if (exactReferences.Length > 0) { return(exactReferences); } // No exact matches found. Fall back to fuzzy searching. // Only bother doing this for host workspaces. We don't want this for // things like the Interactive workspace as this will cause us to // create expensive bk-trees which we won't even be able to save for // future use. if (!IsHostOrTestWorkspace(project)) { return(ImmutableArray <Reference> .Empty); } var fuzzyReferences = await FindResultsAsync(projectToAssembly, referenceToCompilation, project, finder, exact : false, cancellationToken : cancellationToken).ConfigureAwait(false); return(fuzzyReferences); }
private async Task FindResultsInUnreferencedProjects( Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, CancellationToken cancellationToken) { // If we didn't find enough hits searching just in the project, then check // in any unreferenced projects. if (allSymbolReferences.Count >= MaxResults) { return; } var viableUnreferencedProjects = GetViableUnreferencedProjects(project); foreach (var unreferencedProject in viableUnreferencedProjects) { // Search in this unreferenced project. But don't search in any of its' // direct references. i.e. we don't want to search in its metadata references // or in the projects it references itself. We'll be searching those entities // individually. AddRange(allSymbolReferences, await finder.FindInProjectAsync(unreferencedProject, includeDirectReferences: false).ConfigureAwait(false)); if (allSymbolReferences.Count >= MaxResults) { return; } } }
private async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> > projectToAssembly, Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { // If we didn't find enough hits searching just in the project, then check // in any unreferenced projects. if (allSymbolReferences.Count >= MaxResults) { return; } var viableUnreferencedProjects = GetViableUnreferencedProjects(project); foreach (var unreferencedProject in viableUnreferencedProjects) { // Search in this unreferenced project. But don't search in any of its' // direct references. i.e. we don't want to search in its metadata references // or in the projects it references itself. We'll be searching those entities // individually. AddRange(allSymbolReferences, await finder.FindInSourceProjectSymbolsAsync(projectToAssembly, unreferencedProject, exact: exact).ConfigureAwait(false)); if (allSymbolReferences.Count >= MaxResults) { return; } } }
private async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> > projectToAssembly, Project project, ArrayBuilder <Reference> allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { // If we didn't find enough hits searching just in the project, then check // in any unreferenced projects. if (allSymbolReferences.Count >= maxResults) { return; } var viableUnreferencedProjects = GetViableUnreferencedProjects(project); // Search all unreferenced projects in parallel. var findTasks = new HashSet <Task <ImmutableArray <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 unreferencedProject in viableUnreferencedProjects) { // Search in this unreferenced project. But don't search in any of its' // direct references. i.e. we don't want to search in its metadata references // or in the projects it references itself. We'll be searching those entities // individually. findTasks.Add(finder.FindInSourceSymbolsInProjectAsync( projectToAssembly, unreferencedProject, exact, linkedTokenSource.Token)); } await WaitForTasksAsync(allSymbolReferences, maxResults, findTasks, nestedTokenSource, cancellationToken).ConfigureAwait(false); }
private async Task FindResultsInAllSymbolsInStartingProjectAsync( ArrayBuilder <Reference> allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { var references = await finder.FindInAllSymbolsInStartingProjectAsync(exact, cancellationToken).ConfigureAwait(false); AddRange(allSymbolReferences, references, maxResults); }
private async Task FindResultsInUnreferencedMetadataSymbolsAsync( ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, List <Reference> 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.FindInMetadataSymbolsAsync( assembly, reference, exact, linkedTokenSource.Token)); } } await WaitForTasksAsync(allSymbolReferences, findTasks, nestedTokenSource, cancellationToken).ConfigureAwait(false); } }
private async Task FindResults( ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> > projectToAssembly, ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { // First search the current project to see if any symbols (source or metadata) match the // search string. await FindResultsInAllProjectSymbolsAsync(project, allSymbolReferences, finder, exact).ConfigureAwait(false); // Now search unreferenced projects, and see if they have any source symbols that match // the search string. await FindResultsInUnreferencedProjectSourceSymbolsAsync(projectToAssembly, project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); // Finally, check and see if we have any metadata symbols that match the search string. await FindResultsInUnreferencedMetadataSymbolsAsync(referenceToCompilation, project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); }
private async Task FindResults( ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> > projectToAssembly, ConcurrentDictionary <PortableExecutableReference, Compilation> referenceToCompilation, Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { // First search the current project to see if any symbols (source or metadata) match the // search string. await FindResultsInAllProjectSymbolsAsync(project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); // Only bother doing this for host workspaces. We don't want this for // things like the Interactive workspace as we can't even add project // references to the interactive window. We could consider adding metadata // references with #r in the future. if (IsHostOrTestWorkspace(project)) { // Now search unreferenced projects, and see if they have any source symbols that match // the search string. await FindResultsInUnreferencedProjectSourceSymbolsAsync(projectToAssembly, project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); // Finally, check and see if we have any metadata symbols that match the search string. await FindResultsInUnreferencedMetadataSymbolsAsync(referenceToCompilation, project, allSymbolReferences, finder, exact, 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 FindResultsInUnreferencedMetadataReferences( 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>()); // Check all the other projects in the system so see if they have a metadata reference // with a potential result. foreach (var otherProject in project.Solution.Projects) { if (otherProject == project) { continue; } await FindResultsInMetadataReferences( otherProject, allSymbolReferences, finder, seenReferences, exact, cancellationToken).ConfigureAwait(false); if (allSymbolReferences.Count >= MaxResults) { break; } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors <SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = document.Project.Solution.Workspace.Options.GetOption( OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language); using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var allSymbolReferences = new List <SymbolReference>(); var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnostic, node, cancellationToken); await FindResultsInCurrentProject(project, allSymbolReferences, finder).ConfigureAwait(false); await FindResultsInUnreferencedProjects(project, allSymbolReferences, finder, cancellationToken).ConfigureAwait(false); await FindResultsInUnreferencedMetadataReferences(project, allSymbolReferences, finder, cancellationToken).ConfigureAwait(false); if (allSymbolReferences.Count == 0) { return; } cancellationToken.ThrowIfCancellationRequested(); foreach (var reference in allSymbolReferences) { var description = this.GetDescription(reference.Symbol, semanticModel, node); if (description != null) { var action = new MyCodeAction(description, c => this.AddImportAndReferenceAsync(node, reference, document, placeSystemNamespaceFirst, c)); context.RegisterCodeFix(action, diagnostic); } } } } } }
private async Task FindResultsInCurrentProject(Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder) { AddRange(allSymbolReferences, await finder.FindInProjectAsync(project, includeDirectReferences: true).ConfigureAwait(false)); }
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; } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors <SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = document.Project.Solution.Workspace.Options.GetOption( OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language); using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) { if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var allSymbolReferences = new List <SymbolReference>(); var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnostic, node, cancellationToken); // Caches so we don't produce the same data multiple times while searching // all over the solution. var projectToAssembly = new ConcurrentDictionary <Project, AsyncLazy <IAssemblySymbol> >(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary <PortableExecutableReference, Compilation>(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); // Look for exact matches first: await FindResults(projectToAssembly, referenceToCompilation, project, allSymbolReferences, finder, exact : true, cancellationToken : cancellationToken).ConfigureAwait(false); if (allSymbolReferences.Count == 0) { // No exact matches found. Fall back to fuzzy searching. await FindResults(projectToAssembly, referenceToCompilation, project, allSymbolReferences, finder, exact : false, cancellationToken : cancellationToken).ConfigureAwait(false); } // Nothing found at all. No need to proceed. if (allSymbolReferences.Count == 0) { return; } cancellationToken.ThrowIfCancellationRequested(); foreach (var reference in allSymbolReferences) { var description = this.GetDescription(reference.SearchResult.Symbol, semanticModel, node); if (description != null) { var action = new MyCodeAction(description, c => this.AddImportAndReferenceAsync(node, reference, document, placeSystemNamespaceFirst, c)); context.RegisterCodeFix(action, diagnostic); } } } } } }
private async Task FindResults(Project project, List <SymbolReference> allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { await FindResultsInCurrentProject(project, allSymbolReferences, finder, exact).ConfigureAwait(false); await FindResultsInUnreferencedProjects(project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); await FindResultsInUnreferencedMetadataReferences(project, allSymbolReferences, finder, exact, cancellationToken).ConfigureAwait(false); }