private FixAllContext.DiagnosticProvider GetDiagnosticProvider(FixAllContext context) { // This is a hack to get the DiagnosticProvider. But it's not working in newer versions of Roslyn. try { object fixAllState = GetNonpublicProperty(context, "State"); // returns an internal type FixAllState FixAllContext.DiagnosticProvider diagnosticProvider = (FixAllContext.DiagnosticProvider)GetNonpublicProperty(fixAllState, "DiagnosticProvider"); return(diagnosticProvider); } catch { return(null); } }
private FixAllContext GetNextStageFixAllContext(FixAllContext originalContext, Solution revisedSolution) { FixAllContext.DiagnosticProvider diagnosticProvider = GetDiagnosticProvider(originalContext); if (diagnosticProvider == null) { return(null); // Roslyn changed -- won't be able to do multi-stage work, but analyzer should still be able to perform single stage } FixAllContext revisedContext; if (originalContext.Document != null) { var id = originalContext.Document.Id; var documentInRevised = revisedSolution.GetDocument(id); revisedContext = new FixAllContext(documentInRevised, originalContext.CodeFixProvider, originalContext.Scope, originalContext.CodeActionEquivalenceKey, originalContext.DiagnosticIds, diagnosticProvider, originalContext.CancellationToken); } else { var id = originalContext.Project.Id; var projectInRevised = revisedSolution.GetProject(id); revisedContext = new FixAllContext(projectInRevised, originalContext.CodeFixProvider, originalContext.Scope, originalContext.CodeActionEquivalenceKey, originalContext.DiagnosticIds, diagnosticProvider, originalContext.CancellationToken); } return(revisedContext); }
private static async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Project project, int numberOfIterations, CancellationToken cancellationToken) { int expectedNumberOfIterations = numberOfIterations; if (numberOfIterations < 0) { numberOfIterations = -numberOfIterations; } var previousDiagnostics = ImmutableArray.Create <Diagnostic>(); var fixAllProvider = codeFixProvider.GetFixAllProvider(); if (fixAllProvider == null) { return(null); } bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, project.Documents.ToArray(), cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } if (--numberOfIterations < 0) { Assert.True(false, "The upper limit for the number of fix all iterations was exceeded"); } Diagnostic firstDiagnostic = null; string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)) { // do not pass unsupported diagnostics to a code fix provider continue; } var actions = new List <CodeAction>(); var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (actions.Count > (codeFixIndex ?? 0)) { firstDiagnostic = diagnostic; equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey; break; } } if (firstDiagnostic == null) { return(project); } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); IEnumerable <string> analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id); IEnumerable <string> compilerDiagnosticIds = codeFixProvider.FixableDiagnosticIds.Where(x => x.StartsWith("CS", StringComparison.Ordinal)); IEnumerable <string> disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key); IEnumerable <string> relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct(); FixAllContext fixAllContext = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), codeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return(project); } var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false); if (fixedProject != project) { done = false; project = await RecreateProjectDocumentsAsync(fixedProject, cancellationToken).ConfigureAwait(false); } }while (!done); if (expectedNumberOfIterations >= 0) { Assert.Equal($"{expectedNumberOfIterations} iterations", $"{expectedNumberOfIterations - numberOfIterations} iterations"); } return(project); }
private async Task <Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, ImmutableArray <CodeFixProvider> codeFixProviders, int?codeFixIndex, string codeFixEquivalenceKey, Project project, int numberOfIterations, IVerifier verifier, CancellationToken cancellationToken) { var expectedNumberOfIterations = numberOfIterations; if (numberOfIterations < 0) { numberOfIterations = -numberOfIterations; } var previousDiagnostics = ImmutableArray.Create <Diagnostic>(); bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, CompilerDiagnostics, cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } verifier.False(--numberOfIterations < -1, "The upper limit for the number of fix all iterations was exceeded"); Diagnostic firstDiagnostic = null; CodeFixProvider effectiveCodeFixProvider = null; string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { var actions = new List <(CodeAction, CodeFixProvider)>(); foreach (var codeFixProvider in codeFixProviders) { if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id)) { // do not pass unsupported diagnostics to a code fix provider continue; } var context = new CodeFixContext(project.GetDocument(diagnostic.Location.SourceTree), diagnostic, (a, d) => actions.Add((a, codeFixProvider)), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); } var actionToApply = TryGetCodeActionToApply(actions.Select(a => a.Item1).ToList(), codeFixIndex, codeFixEquivalenceKey, verifier); if (actionToApply != null) { firstDiagnostic = diagnostic; effectiveCodeFixProvider = actions.SingleOrDefault(a => a.Item1 == actionToApply).Item2; equivalenceKey = actionToApply.EquivalenceKey; break; } } var fixAllProvider = effectiveCodeFixProvider?.GetFixAllProvider(); if (firstDiagnostic == null || fixAllProvider == null) { numberOfIterations++; break; } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); var analyzerDiagnosticIds = analyzers.SelectMany(x => x.SupportedDiagnostics).Select(x => x.Id); var compilerDiagnosticIds = codeFixProviders.SelectMany(codeFixProvider => codeFixProvider.FixableDiagnosticIds).Where(x => x.StartsWith("CS", StringComparison.Ordinal) || x.StartsWith("BC", StringComparison.Ordinal)); var disabledDiagnosticIds = project.CompilationOptions.SpecificDiagnosticOptions.Where(x => x.Value == ReportDiagnostic.Suppress).Select(x => x.Key); var relevantIds = analyzerDiagnosticIds.Concat(compilerDiagnosticIds).Except(disabledDiagnosticIds).Distinct(); var fixAllContext = new FixAllContext(project.GetDocument(firstDiagnostic.Location.SourceTree), effectiveCodeFixProvider, scope, equivalenceKey, relevantIds, fixAllDiagnosticProvider, cancellationToken); var action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return(project); } var fixedProject = await ApplyFixAsync(project, action, cancellationToken).ConfigureAwait(false); if (fixedProject != project) { done = false; project = await RecreateProjectDocumentsAsync(fixedProject, verifier, cancellationToken).ConfigureAwait(false); } }while (!done); if (expectedNumberOfIterations >= 0) { verifier.Equal(expectedNumberOfIterations, expectedNumberOfIterations - numberOfIterations, $"Expected '{expectedNumberOfIterations}' iterations but found '{expectedNumberOfIterations - numberOfIterations}' iterations."); } else { verifier.True(numberOfIterations >= 0, "The upper limit for the number of code fix iterations was exceeded"); } return(project); }
private static async Task <Document> GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Document document, int maxNumberOfIterations, CancellationToken cancellationToken) { var previousDiagnostics = ImmutableArray.Create <Diagnostic>(); var fixAllProvider = codeFixProvider.GetFixAllProvider(); if (fixAllProvider == null) { return(null); } bool done; do { var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false); if (analyzerDiagnostics.Length == 0) { break; } if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics)) { break; } if (--maxNumberOfIterations < 0) { Assert.True(false, "The upper limit for the number of fix all iterations was exceeded"); } string equivalenceKey = null; foreach (var diagnostic in analyzerDiagnostics) { var actions = new List <CodeAction>(); var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (actions.Count > (codeFixIndex ?? 0)) { equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey; break; } } previousDiagnostics = analyzerDiagnostics; done = true; FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics); FixAllContext fixAllContext = new FixAllContext(document, codeFixProvider, scope, equivalenceKey, codeFixProvider.FixableDiagnosticIds, fixAllDiagnosticProvider, cancellationToken); CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); if (action == null) { return(document); } var fixedDocument = await ApplyFixAsync(document, action, cancellationToken).ConfigureAwait(false); if (fixedDocument != document) { done = false; var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document. document = document.WithText(newText); } }while (!done); return(document); }