Ejemplo n.º 1
0
        private async Task <Solution?> FixAllContextsAsync(
            FixAllContext originalFixAllContext,
            ImmutableArray <FixAllContext> fixAllContexts
            )
        {
            var progressTracker = originalFixAllContext.GetProgressTracker();

            progressTracker.Description = this.GetFixAllTitle(originalFixAllContext);

            var solution = originalFixAllContext.Solution;

            // We have 3 pieces of work per project.  Computing diagnostics, computing fixes, and applying fixes.
            progressTracker.AddItems(fixAllContexts.Length * 3);

            // Process each context one at a time, allowing us to dump any information we computed for each once done with it.
            var currentSolution = solution;

            foreach (var fixAllContext in fixAllContexts)
            {
                Contract.ThrowIfFalse(
                    fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project
                    );
                currentSolution = await FixSingleContextAsync(
                    currentSolution,
                    fixAllContext,
                    progressTracker
                    )
                                  .ConfigureAwait(false);
            }

            return(currentSolution);
        }
            private async Task <CodeAction> GetFixAsync(
                ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap,
                FixAllContext fixAllContext)
            {
                // Process all documents in parallel.
                var progressTracker = fixAllContext.GetProgressTracker();

                progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all;
                progressTracker.AddItems(documentsAndDiagnosticsToFixMap.Count);

                var updatedDocumentTasks = documentsAndDiagnosticsToFixMap.Select(
                    kvp => FixDocumentAsync(kvp.Key, kvp.Value, fixAllContext, progressTracker));

                await Task.WhenAll(updatedDocumentTasks).ConfigureAwait(false);

                var currentSolution = fixAllContext.Solution;

                foreach (var task in updatedDocumentTasks)
                {
                    // 'await' the tasks so that if any completed in a canceled manner then we'll
                    // throw the right exception here.  Calling .Result on the tasks might end up
                    // with AggregateExceptions being thrown instead.
                    var updatedDocument = await task.ConfigureAwait(false);

                    currentSolution = currentSolution.WithDocumentSyntaxRoot(
                        updatedDocument.Id,
                        await updatedDocument.GetRequiredSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false));
                }

                var title = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext);

                return(new CustomCodeActions.SolutionChangeAction(title, _ => Task.FromResult(currentSolution)));
            }
        private async Task <Solution> GetSolutionFixesAsync(FixAllContext fixAllContext, ImmutableArray <Document> documents)
        {
            var documentDiagnosticsToFix = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(fixAllContext).ConfigureAwait(false);

            using var _1 = PooledHashSet <Document> .GetInstance(out var documentsToFix);

            using var _2 = PooledDictionary <DocumentId, Task <SyntaxNode?> > .GetInstance(out var documentIdToNewNode);

            // Determine the set of documents to actually fix.  We can also use this to update the progress bar with
            // the amount of remaining work to perform.  We'll update the progress bar as we perform each fix in
            // FixAllInDocumentAsync.

            foreach (var document in documents)
            {
                // Don't bother examining any documents that aren't in the list of docs that
                // actually have diagnostics.
                if (!documentDiagnosticsToFix.TryGetValue(document, out var diagnostics))
                {
                    continue;
                }

                documentsToFix.Add(document);
            }

            var progressTracker = fixAllContext.GetProgressTracker();

            progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all;
            progressTracker.AddItems(documentsToFix.Count);

            foreach (var document in documentsToFix)
            {
                // Upper loop ensures that this indexing will always succeed.
                var diagnostics = documentDiagnosticsToFix[document];
                documentIdToNewNode.Add(document.Id, FixAllInDocumentAsync(fixAllContext, progressTracker, document, diagnostics));
            }

            // Allow the processing of all the documents to happen concurrently.
            await Task.WhenAll(documentIdToNewNode.Values).ConfigureAwait(false);

            var solution = fixAllContext.Solution;

            foreach (var(docId, syntaxNodeTask) in documentIdToNewNode)
            {
                var newDocumentRoot = await syntaxNodeTask.ConfigureAwait(false);

                if (newDocumentRoot == null)
                {
                    continue;
                }

                solution = solution.WithDocumentSyntaxRoot(docId, newDocumentRoot);
            }

            return(solution);
        }
Ejemplo n.º 4
0
        private async Task <Solution?> FixAllContextsAsync(
            FixAllContext originalFixAllContext,
            ImmutableArray <FixAllContext> fixAllContexts
            )
        {
            var cancellationToken = originalFixAllContext.CancellationToken;
            var progressTracker   = originalFixAllContext.GetProgressTracker();

            progressTracker.Description = FixAllContextHelper.GetDefaultFixAllTitle(
                originalFixAllContext
                );

            // We have 2*P + 1 pieces of work.  Computing diagnostics and fixes/changes per context, and then one pass
            // applying fixes.
            progressTracker.AddItems(fixAllContexts.Length * 2 + 1);

            // Mapping from document to the cumulative text changes created for that document.
            var docIdToTextMerger = new Dictionary <DocumentId, TextChangeMerger>();

            // Process each context one at a time, allowing us to dump most of the information we computed for each once
            // done with it.  The only information we need to preserve is the data we store in docIdToTextMerger
            foreach (var fixAllContext in fixAllContexts)
            {
                Contract.ThrowIfFalse(
                    fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project
                    );
                await FixSingleContextAsync(fixAllContext, progressTracker, docIdToTextMerger)
                .ConfigureAwait(false);
            }

            // Finally, merge in all text changes into the solution.  We can't do this per-project as we have to have
            // process *all* diagnostics in the solution to find the changes made to all documents.
            using (progressTracker.ItemCompletedScope())
            {
                if (docIdToTextMerger.Count == 0)
                {
                    return(null);
                }

                var currentSolution = originalFixAllContext.Solution;
                foreach (var group in docIdToTextMerger.GroupBy(kvp => kvp.Key.ProjectId))
                {
                    currentSolution = await ApplyChangesAsync(
                        currentSolution,
                        group.SelectAsArray(kvp => (kvp.Key, kvp.Value)),
                        cancellationToken
                        )
                                      .ConfigureAwait(false);
                }

                return(currentSolution);
            }
        }
Ejemplo n.º 5
0
        private async Task <ImmutableArray <(Diagnostic diagnostic, CodeAction action)> > GetDiagnosticsAndCodeActionsAsync(
            ImmutableDictionary <Document, ImmutableArray <Diagnostic> > documentsAndDiagnosticsToFixMap,
            FixAllContext fixAllContext)
        {
            var cancellationToken = fixAllContext.CancellationToken;
            var fixAllState       = fixAllContext.State;
            var fixesBag          = new ConcurrentBag <(Diagnostic diagnostic, CodeAction action)>();

            using (Logger.LogBlock(
                       FunctionId.CodeFixes_FixAllOccurrencesComputation_Document_Fixes,
                       FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId),
                       cancellationToken))
            {
                cancellationToken.ThrowIfCancellationRequested();

                var progressTracker = fixAllContext.GetProgressTracker();
                progressTracker.Description = WorkspaceExtensionsResources.Applying_fix_all;

                using var _1 = ArrayBuilder <Task> .GetInstance(out var tasks);

                using var _2 = ArrayBuilder <Document> .GetInstance(out var documentsToFix);

                // Determine the set of documents to actually fix.  We can also use this to update the progress bar with
                // the amount of remaining work to perform.  We'll update the progress bar as we compute each fix in
                // AddDocumentFixesAsync.
                foreach (var(document, diagnosticsToFix) in documentsAndDiagnosticsToFixMap)
                {
                    if (!diagnosticsToFix.IsDefaultOrEmpty)
                    {
                        documentsToFix.Add(document);
                    }
                }

                progressTracker.AddItems(documentsToFix.Count);

                foreach (var document in documentsToFix)
                {
                    var diagnosticsToFix = documentsAndDiagnosticsToFixMap[document];
                    tasks.Add(AddDocumentFixesAsync(
                                  document, diagnosticsToFix, fixesBag, fixAllState, progressTracker, cancellationToken));
                }

                await Task.WhenAll(tasks).ConfigureAwait(false);
            }

            return(fixesBag.ToImmutableArray());
        }
        public static async Task <ImmutableDictionary <Document, ImmutableArray <Diagnostic> > > GetDocumentDiagnosticsToFixAsync(FixAllContext fixAllContext)
        {
            var cancellationToken = fixAllContext.CancellationToken;

            var allDiagnostics = ImmutableArray <Diagnostic> .Empty;

            var document = fixAllContext.Document;
            var project  = fixAllContext.Project;

            var progressTracker = fixAllContext.GetProgressTracker();

            switch (fixAllContext.Scope)
            {
            case FixAllScope.Document:
                if (document != null && !await document.IsGeneratedCodeAsync(cancellationToken).ConfigureAwait(false))
                {
                    var documentDiagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(document).ConfigureAwait(false);

                    return(ImmutableDictionary <Document, ImmutableArray <Diagnostic> > .Empty.SetItem(document, documentDiagnostics));
                }

                break;

            case FixAllScope.Project:
                allDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false);

                break;

            case FixAllScope.Solution:
                var projectsToFix = project.Solution.Projects
                                    .Where(p => p.Language == project.Language)
                                    .ToImmutableArray();

                // Update the progress dialog with the count of projects to actually fix. We'll update the progress
                // bar as we get all the documents in AddDocumentDiagnosticsAsync.

                progressTracker.AddItems(projectsToFix.Length);

                var diagnostics = new ConcurrentDictionary <ProjectId, ImmutableArray <Diagnostic> >();
                using (var _ = ArrayBuilder <Task> .GetInstance(projectsToFix.Length, out var tasks))
                {
                    foreach (var projectToFix in projectsToFix)
                    {
                        tasks.Add(Task.Run(async() => await AddDocumentDiagnosticsAsync(diagnostics, projectToFix).ConfigureAwait(false), cancellationToken));
                    }

                    await Task.WhenAll(tasks).ConfigureAwait(false);

                    allDiagnostics = allDiagnostics.AddRange(diagnostics.SelectMany(i => i.Value));
                }

                break;
            }

            if (allDiagnostics.IsEmpty)
            {
                return(ImmutableDictionary <Document, ImmutableArray <Diagnostic> > .Empty);
            }

            return(await GetDocumentDiagnosticsToFixAsync(
                       fixAllContext.Solution, allDiagnostics, fixAllContext.CancellationToken).ConfigureAwait(false));

            async Task AddDocumentDiagnosticsAsync(ConcurrentDictionary <ProjectId, ImmutableArray <Diagnostic> > diagnostics, Project projectToFix)
            {
                try
                {
                    var projectDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(projectToFix).ConfigureAwait(false);

                    diagnostics.TryAdd(projectToFix.Id, projectDiagnostics);
                }
                finally
                {
                    progressTracker.ItemCompleted();
                }
            }
        }
Ejemplo n.º 7
0
 private Task <Solution?> FixAllContextsHelperAsync(FixAllContext originalFixAllContext, ImmutableArray <FixAllContext> fixAllContexts)
 => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync(originalFixAllContext, fixAllContexts,
                                                           originalFixAllContext.GetProgressTracker(),
                                                           this.GetFixAllTitle(originalFixAllContext),
                                                           DetermineDiagnosticsAndGetFixedDocumentsAsync);