Ejemplo n.º 1
0
        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;
                }
            }
        }
Ejemplo n.º 5
0
        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);
                }
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
        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));
 }
Ejemplo n.º 15
0
        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;
                }
            }
        }
Ejemplo n.º 16
0
        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);
 }