Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
Expand All @@ -13,6 +14,7 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.LanguageService;
Expand Down Expand Up @@ -85,6 +87,19 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)

private async Task<Solution> ProcessResultAsync(
Solution originalSolution, Solution currentSolution, Diagnostic diagnostic, CancellationToken cancellationToken)
{
try
{
return await ProcessResultWorkerAsync(originalSolution, currentSolution, diagnostic, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heard from community member about things occasionally failing. adding some dedicated NFWs for this.

{
return currentSolution;
}
}

private async Task<Solution> ProcessResultWorkerAsync(
Solution originalSolution, Solution currentSolution, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var (field, property) = await MapDiagnosticToCurrentSolutionAsync(
diagnostic, originalSolution, currentSolution, cancellationToken).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
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;
Expand All @@ -34,23 +36,65 @@ private sealed class UseAutoPropertyFixAllProvider(TProvider provider) : FixAllP
// 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.
var originalSolution = originalContext.Solution;
var currentSolution = originalSolution;

foreach (var currentContext in contexts)
{
var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false);
foreach (var (_, diagnostics) in documentToDiagnostics)
// Add a progress item for each context we need to process.
originalContext.Progress.AddItems(contexts.Length);

var finalSolution = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync(
contexts,
produceItems: async static (currentContext, callback, args, cancellationToken) =>
{
foreach (var diagnostic in diagnostics)
// 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 projectChanges in currentSolution.GetChanges(originalSolution).GetProjectChanges())
{
currentSolution = await provider.ProcessResultAsync(
originalSolution, currentSolution, diagnostic, cancellationToken).ConfigureAwait(false);
foreach (var changedDocumentId in projectChanges.GetChangedDocuments())
{
var changedDocument = currentSolution.GetRequiredDocument(changedDocumentId);
var changedRoot = await changedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
callback((changedDocumentId, changedRoot));
}
}
}
}
},
consumeItems: async static (documentsIdsAndNewRoots, args, cancellationToken) =>
{
var (originalContext, _) = args;
var currentSolution = originalContext.Solution;

// Take all the changed documents and update the final solution with them.
await foreach (var (documentId, newRoot) in documentsIdsAndNewRoots)
currentSolution = currentSolution.WithDocumentSyntaxRoot(documentId, newRoot);

return currentSolution;
},
args: (originalContext, provider),
cancellationToken).ConfigureAwait(false);

return currentSolution;
return finalSolution;
}
}
}