From b3bd4d939b0978cae4264beeb5b6095a655e7f64 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 5 Jan 2024 10:41:15 -0600 Subject: [PATCH] Support span in CreateFixAllContext --- .../CodeFixTest`1.cs | 27 +++++----- .../Extensions/FixAllContextExtensions.cs | 49 +++++++++++++++++++ .../PublicAPI.Unshipped.txt | 2 +- 3 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/Extensions/FixAllContextExtensions.cs diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs index 972e3fc6..2327345b 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; -using System.IO; using System.Linq; using System.Runtime.ExceptionServices; using System.Threading; @@ -17,6 +16,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; +using Microsoft.CodeAnalysis.Testing.Extensions; using Microsoft.CodeAnalysis.Testing.Model; using Microsoft.CodeAnalysis.Text; @@ -241,6 +241,7 @@ namespace Microsoft.CodeAnalysis.Testing /// Creates a new . /// /// Document within which fix all occurrences was triggered, or null when applying fix all to a diagnostic with no source location. + /// Span for the diagnostic for which fix all occurrences was triggered. /// Project within which fix all occurrences was triggered. /// Underlying which triggered this fix all. /// to fix all occurrences. @@ -253,6 +254,7 @@ namespace Microsoft.CodeAnalysis.Testing /// New protected virtual FixAllContext CreateFixAllContext( Document? document, + TextSpan? diagnosticSpan, Project project, CodeFixProvider codeFixProvider, FixAllScope scope, @@ -260,9 +262,11 @@ namespace Microsoft.CodeAnalysis.Testing IEnumerable diagnosticIds, FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) - => document != null ? - new FixAllContext(document, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) : - new FixAllContext(project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken); + { + return document != null + ? FixAllContextExtensions.Create(document, diagnosticSpan, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) + : new FixAllContext(project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken); + } /// protected override bool IsCompilerDiagnosticIncluded(Diagnostic diagnostic, CompilerDiagnostics compilerDiagnostics) @@ -781,10 +785,10 @@ namespace Microsoft.CodeAnalysis.Testing fixableDiagnostics = diagnosticToFix is not null ? ImmutableArray.Create(fixableDiagnostics.Single(x => x.diagnostic == diagnosticToFix)) : ImmutableArray<(Project project, Diagnostic diagnostic)>.Empty; } - Diagnostic? firstDiagnostic = null; + (Project project, Diagnostic diagnostic)? firstDiagnostic = null; CodeFixProvider? effectiveCodeFixProvider = null; string? equivalenceKey = null; - foreach (var (_, diagnostic) in fixableDiagnostics) + foreach (var (diagnosticProject, diagnostic) in fixableDiagnostics) { var actions = new List<(CodeAction, CodeFixProvider)>(); @@ -806,7 +810,7 @@ namespace Microsoft.CodeAnalysis.Testing var actionToApply = TryGetCodeActionToApply(currentIteration, actions.Select(a => a.Item1).ToImmutableArray(), codeFixIndex, codeFixEquivalenceKey, codeActionVerifier, verifier); if (actionToApply != null) { - firstDiagnostic = diagnostic; + firstDiagnostic = (diagnosticProject, diagnostic); effectiveCodeFixProvider = actions.SingleOrDefault(a => a.Item1 == actionToApply).Item2; equivalenceKey = actionToApply.EquivalenceKey; break; @@ -826,9 +830,10 @@ namespace Microsoft.CodeAnalysis.Testing FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); - var fixableDocument = project.Solution.GetDocument(firstDiagnostic.Location.SourceTree); + var fixableDocument = project.Solution.GetDocument(firstDiagnostic.Value.diagnostic.Location.SourceTree); + var diagnosticSpan = fixableDocument is not null ? firstDiagnostic.Value.diagnostic.Location.SourceSpan : (TextSpan?)null; var relevantIds = fixAllProvider.GetSupportedFixAllDiagnosticIds(effectiveCodeFixProvider); - var fixAllContext = CreateFixAllContext(fixableDocument, fixableDocument.Project, effectiveCodeFixProvider!, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); + var fixAllContext = CreateFixAllContext(fixableDocument, diagnosticSpan, firstDiagnostic.Value.project, effectiveCodeFixProvider!, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) @@ -837,9 +842,9 @@ namespace Microsoft.CodeAnalysis.Testing } var originalProjectId = project.Id; - var (fixedProject, currentError) = await ApplyCodeActionAsync(fixableDocument.Project, action, verifier, cancellationToken).ConfigureAwait(false); + var (fixedProject, currentError) = await ApplyCodeActionAsync(firstDiagnostic.Value.project, action, verifier, cancellationToken).ConfigureAwait(false); firstValidationError ??= currentError; - if (fixedProject != fixableDocument.Project) + if (fixedProject != firstDiagnostic.Value.project) { done = false; project = fixedProject.Solution.GetProject(originalProjectId); diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/Extensions/FixAllContextExtensions.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/Extensions/FixAllContextExtensions.cs new file mode 100644 index 00000000..723a5fe7 --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/Extensions/FixAllContextExtensions.cs @@ -0,0 +1,49 @@ +// 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; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; +using System.Threading; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Testing.Extensions; + +internal static class FixAllContextExtensions +{ + private static readonly Func, FixAllContext.DiagnosticProvider, CancellationToken, FixAllContext> s_createFixAllContextDocument; + + static FixAllContextExtensions() + { + var constructorInfo = typeof(CompilationWithAnalyzers).GetConstructor(new[] { typeof(Document), typeof(TextSpan?), typeof(CodeFixProvider), typeof(FixAllScope), typeof(string), typeof(IEnumerable), typeof(ImmutableArray), typeof(FixAllContext.DiagnosticProvider), typeof(CancellationToken) }); + if (constructorInfo is not null) + { + s_createFixAllContextDocument = (document, diagnosticSpan, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) => + { + return (FixAllContext)Activator.CreateInstance(typeof(FixAllContext), document, diagnosticSpan, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken)!; + }; + } + else + { + s_createFixAllContextDocument = (document, diagnosticSpan, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken) => + new FixAllContext(document, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken); + } + } + + public static FixAllContext Create( + Document document, + TextSpan? diagnosticSpan, + CodeFixProvider codeFixProvider, + FixAllScope scope, + string? codeActionEquivalenceKey, + IEnumerable diagnosticIds, + FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, + CancellationToken cancellationToken) + { + return s_createFixAllContextDocument(document, diagnosticSpan, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider, cancellationToken); + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/PublicAPI.Unshipped.txt index 63be49b1..f823e336 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/PublicAPI.Unshipped.txt @@ -50,5 +50,5 @@ static Microsoft.CodeAnalysis.Testing.CodeFixVerifier.VerifyCodeFixAsync(string source, Microsoft.CodeAnalysis.Testing.DiagnosticResult[] expected, string fixedSource) -> System.Threading.Tasks.Task static Microsoft.CodeAnalysis.Testing.CodeFixVerifier.VerifyCodeFixAsync(string source, string fixedSource) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.CodeFixTest.CreateCodeFixContext(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableArray diagnostics, System.Action> registerCodeFix, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeFixes.CodeFixContext -virtual Microsoft.CodeAnalysis.Testing.CodeFixTest.CreateFixAllContext(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider codeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllScope scope, string codeActionEquivalenceKey, System.Collections.Generic.IEnumerable diagnosticIds, Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeFixes.FixAllContext +virtual Microsoft.CodeAnalysis.Testing.CodeFixTest.CreateFixAllContext(Microsoft.CodeAnalysis.Document document, Microsoft.CodeAnalysis.Text.TextSpan? diagnosticSpan, Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider codeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllScope scope, string codeActionEquivalenceKey, System.Collections.Generic.IEnumerable diagnosticIds, Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.CodeFixes.FixAllContext virtual Microsoft.CodeAnalysis.Testing.CodeFixTest.TrySelectDiagnosticToFix(System.Collections.Immutable.ImmutableArray fixableDiagnostics) -> Microsoft.CodeAnalysis.Diagnostic