|
// 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);
}
}
}
|