File: UseAutoProperty\UseAutoPropertyFixAllProvider.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixesAndRefactorings;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.UseAutoProperty;
 
internal abstract partial class AbstractUseAutoPropertyCodeFixProvider<
    TProvider,
    TTypeDeclarationSyntax,
    TPropertyDeclaration,
    TVariableDeclarator,
    TConstructorDeclaration,
    TExpression>
{
    private sealed class UseAutoPropertyFixAllProvider(TProvider provider) : FixAllProvider
    {
        public override Task<CodeAction?> GetFixAsync(FixAllContext fixAllContext)
            => DefaultFixAllProviderHelpers.GetFixAsync(
                fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync);
 
        private async Task<Solution?> FixAllContextsHelperAsync(FixAllContext originalContext, ImmutableArray<FixAllContext> contexts)
        {
            // Very slow approach, but the only way we know how to do this correctly and without colliding edits. We
            // effectively apply each fix one at a time, moving the solution forward each time.  As we process each
            // diagnostic, we attempt to re-recover the field/property it was referring to in the original solution to
            // the current solution.
            //
            // Note: we can process each project in parallel.  That's because all changes to a field/prop only impact
            // the project they are in, and nothing beyond that.
 
            // Add a progress item for each context we need to process.
            originalContext.Progress.AddItems(contexts.Length);
 
            var documentsIdsAndNewRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync(
                contexts,
                produceItems: async static (currentContext, callback, args, cancellationToken) =>
                {
                    // Within a single context (a project) get all diagnostics, and then handle each diagnostic, one at
                    // a time, to get the final state of the project.
                    var (originalContext, provider) = args;
 
                    // Complete a progress item as we finish each project.
                    using var _ = originalContext.Progress.ItemCompletedScope();
 
                    var originalSolution = originalContext.Solution;
                    var currentSolution = originalSolution;
 
                    var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false);
                    foreach (var (_, diagnostics) in documentToDiagnostics)
                    {
                        foreach (var diagnostic in diagnostics)
                        {
                            currentSolution = await provider.ProcessResultAsync(
                                originalSolution, currentSolution, diagnostic, cancellationToken).ConfigureAwait(false);
                        }
                    }
 
                    // After we finish this context, report the changed documents to the consumeItems callback to process.
                    // This also lets us release all the forked solution info we created above.
                    foreach (var changedDocumentId in originalSolution.GetChangedDocuments(currentSolution))
                    {
                        var changedDocument = currentSolution.GetRequiredDocument(changedDocumentId);
                        var changedRoot = await changedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                        callback((changedDocumentId, changedRoot));
                    }
                },
                args: (originalContext, provider),
                originalContext.CancellationToken).ConfigureAwait(false);
 
            return originalContext.Solution.WithDocumentSyntaxRoots(documentsIdsAndNewRoots);
        }
    }
}