I've 'successfully' written a CodeFix and FixAllProvider, BUT...
The diagnostic I'm trying to handle can occur multiple times in the same document on the same line. However, the behavior under unit test (CSharpCodeFixTest) stumps me.
If a test generates only one instance of the diagnostic, CSharpCodeFix<> calls the CodeFix initially then calls the FixAllProvider multiple times during Verification. The test succeeds.
If a test generates more than one diagnostic, CSharpCodeFix<> calls the CodeFix once. CSharpCodeFix<> never calls the FixAllProvider, and since the CodeFix cannot fix all instances. the test fails the before/after document comparison.
Note that in these samples, namespaces (not shown) disambiguate the classes from their bases. I've removed the fix implementations because I believe them irrelevant to the problem.
First the CodeFix
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CodeFixProvider)), Shared]
public class CodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(EGNT0003NoInlineInstantiationAnalyzer.DiagnosticId); }
}
public sealed override Microsoft.CodeAnalysis.CodeFixes.FixAllProvider GetFixAllProvider()
{
Microsoft.CodeAnalysis.CodeFixes.FixAllProvider provider = FixAllProvider.Instance;
return provider;
}
public static readonly string EquivalenceKey = "EG0003CodeFixProvider";
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (Diagnostic diagnostic in context.Diagnostics.Where(d => FixableDiagnosticIds.Contains(d.Id)))
{
context.RegisterCodeFix(CodeAction.Create(title: "Introduce local variable",
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
equivalenceKey: EquivalenceKey), diagnostic);
}
return Task.CompletedTask;
}
;
Here is the FixAllProvider
public sealed class FixAllProvider : Microsoft.CodeAnalysis.CodeFixes.FixAllProvider
{
private FixAllProvider()
{
}
private static readonly Lazy<FixAllProvider> lazy = new Lazy<FixAllProvider>(() => new FixAllProvider());
public static FixAllProvider Instance
{
get
{
return lazy.Value;
}
}
public override IEnumerable<string> GetSupportedFixAllDiagnosticIds(Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider originalCodeFixProvider)
{
string[] diagnosticIds = new[]
{
EGNT0003NoInlineInstantiationAnalyzer.DiagnosticId,
};
return diagnosticIds;
}
public override async Task<CodeAction> GetFixAsync(FixAllContext fixAllContext)
{
:
Finally here is the CodeAction invoked by the FixAllProvider.
public class FixAllCodeAction : CodeAction
{
private readonly List<KeyValuePair<Document, ImmutableArray<Diagnostic>>> _diagnosticsToFix;
private readonly Solution _solution;
public FixAllCodeAction(string title, Solution solution, List<KeyValuePair<Document, ImmutableArray<Diagnostic>>> diagnosticsToFix)
{
this.Title = title;
_solution = solution;
_diagnosticsToFix = diagnosticsToFix;
}
public override string Title { get; }
public override string EquivalenceKey => "EG0003CodeFixProvider";
protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
Solution newSolution = _solution;
:
I've debugged through CSharpCodeFixTest<> and continue to do so. I'm hoping someone has seen this issue before and can see my mistake.
I expected to see the code fix tests to complete successfully. I verified through other means that the documents produced by the CodeFix and the FixAllProvider are valid and correct.