diff --git a/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs index 8df5f9a04b589..62063e0fbca41 100644 --- a/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs +++ b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysis.cs @@ -55,4 +55,6 @@ internal readonly record struct CopilotCodeFixAnalysis( [property: DataMember(Order = 3)] Dictionary DiagnosticIdToApplicationTime, [property: DataMember(Order = 4)] Dictionary> DiagnosticIdToProviderName, [property: DataMember(Order = 5)] Dictionary ProviderNameToApplicationTime, - [property: DataMember(Order = 6)] Dictionary ProviderNameToHasConflict); + [property: DataMember(Order = 6)] Dictionary ProviderNameToHasConflict, + [property: DataMember(Order = 7)] Dictionary ProviderNameToTotalCount, + [property: DataMember(Order = 8)] Dictionary ProviderNameToSuccessCount); diff --git a/src/Features/Core/Portable/Copilot/CopilotChangeAnalysisUtilities.cs b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysisUtilities.cs index 81eb127658742..832f1b7115d8d 100644 --- a/src/Features/Core/Portable/Copilot/CopilotChangeAnalysisUtilities.cs +++ b/src/Features/Core/Portable/Copilot/CopilotChangeAnalysisUtilities.cs @@ -120,6 +120,8 @@ public static IDisposable LogCopilotChangeAnalysis( d["CodeFixAnalysis_DiagnosticIdToProviderName"] = StringifyDictionary(analysisResult.CodeFixAnalysis.DiagnosticIdToProviderName); d["CodeFixAnalysis_ProviderNameToApplicationTime"] = StringifyDictionary(analysisResult.CodeFixAnalysis.ProviderNameToApplicationTime); d["CodeFixAnalysis_ProviderNameToHasConflict"] = StringifyDictionary(analysisResult.CodeFixAnalysis.ProviderNameToHasConflict); + d["CodeFixAnalysis_ProviderNameToTotalCount"] = StringifyDictionary(analysisResult.CodeFixAnalysis.ProviderNameToTotalCount); + d["CodeFixAnalysis_ProviderNameToSuccessCount"] = StringifyDictionary(analysisResult.CodeFixAnalysis.ProviderNameToSuccessCount); }, args: (featureId, accepted, proposalId, analysisResult)), cancellationToken); } diff --git a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index f5019cf430543..9187b29aa4500 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; @@ -255,22 +256,36 @@ private async Task ComputeCodeFixAnalysisAsync( var diagnosticIdToProviderName = new Dictionary>(); var providerNameToApplicationTime = new Dictionary(); var providerNameToHasConflict = new Dictionary(); + var providerNameToTotalCount = new Dictionary(); + var providerNameToSuccessCount = new Dictionary(); var totalApplicationTimeStopWatch = SharedStopwatch.StartNew(); - await ProducerConsumer<(CodeFixCollection collection, TimeSpan elapsedTime)>.RunParallelAsync( + await ProducerConsumer<(CodeFixCollection collection, bool success, TimeSpan elapsedTime)>.RunParallelAsync( codeFixCollections, produceItems: static async (codeFixCollection, callback, args, cancellationToken) => { - var (@this, solution, _, _, _, _, _) = args; + var (@this, solution, _, _, _, _, _, _, _) = args; var firstAction = GetFirstAction(codeFixCollection.Fixes[0]); var applicationTimeStopWatch = SharedStopwatch.StartNew(); - var result = await firstAction.GetPreviewOperationsAsync(solution, cancellationToken).ConfigureAwait(false); - callback((codeFixCollection, applicationTimeStopWatch.Elapsed)); + var success = true; + try + { + await firstAction + .GetPreviewOperationsAsync(solution, cancellationToken) + .ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + success = false; + } + + callback((codeFixCollection, success, applicationTimeStopWatch.Elapsed)); }, consumeItems: static async (values, args, cancellationToken) => { - var (@this, solution, diagnosticIdToCount, diagnosticIdToApplicationTime, diagnosticIdToProviderName, providerNameToApplicationTime, providerNameToHasConflict) = args; + var (@this, solution, diagnosticIdToCount, diagnosticIdToApplicationTime, diagnosticIdToProviderName, + providerNameToApplicationTime, providerNameToHasConflict, providerNameToTotalCount, providerNameToSuccessCount) = args; // Track which text span each code fix says it will be fixing. We can use this to efficiently determine // which codefixes 'conflict' with some other codefix (in that that multiple features think they can fix @@ -278,7 +293,7 @@ private async Task ComputeCodeFixAnalysisAsync( // order to have a good experience in such a case. var intervalTree = new SimpleMutableIntervalTree(new CodeFixCollectionIntervalIntrospector()); - await foreach (var (codeFixCollection, applicationTime) in values) + await foreach (var (codeFixCollection, success, applicationTime) in values) { var diagnosticId = codeFixCollection.FirstDiagnostic.Id; var providerName = GetProviderName(codeFixCollection); @@ -287,6 +302,10 @@ private async Task ComputeCodeFixAnalysisAsync( IncrementElapsedTime(diagnosticIdToApplicationTime, diagnosticId, applicationTime); diagnosticIdToProviderName.MultiAdd(diagnosticId, providerName); IncrementElapsedTime(providerNameToApplicationTime, providerName, applicationTime); + IncrementCount(providerNameToTotalCount, providerName); + + if (success) + IncrementCount(providerNameToSuccessCount, providerName); intervalTree.AddIntervalInPlace(codeFixCollection); } @@ -311,7 +330,8 @@ private async Task ComputeCodeFixAnalysisAsync( providerNameToHasConflict[providerName] = storedHasConflictValue || newHasConflictValue; } }, - args: (@this: this, newDocument.Project.Solution, diagnosticIdToCount, diagnosticIdToApplicationTime, diagnosticIdToProviderName, providerNameToApplicationTime, providerNameToHasConflict), + args: (@this: this, newDocument.Project.Solution, diagnosticIdToCount, diagnosticIdToApplicationTime, diagnosticIdToProviderName, + providerNameToApplicationTime, providerNameToHasConflict, providerNameToTotalCount, providerNameToSuccessCount), cancellationToken).ConfigureAwait(false); var totalApplicationTime = totalApplicationTimeStopWatch.Elapsed; @@ -322,7 +342,9 @@ private async Task ComputeCodeFixAnalysisAsync( diagnosticIdToApplicationTime, diagnosticIdToProviderName, providerNameToApplicationTime, - providerNameToHasConflict); + providerNameToHasConflict, + providerNameToTotalCount, + providerNameToSuccessCount); Task> ComputeCodeFixCollectionsAsync() {