Skip to content

Commit 784d3cc

Browse files
committed
Clean up and document FixAllContextHelper behavior
This commit also moves back to using FixAllContext.GetAllDiagnosticsAsync for non-1.1 versions of Roslyn, where performance of the operation wasn't causing a major problem.
1 parent 39948d9 commit 784d3cc

1 file changed

Lines changed: 46 additions & 26 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/FixAllContextHelper.cs

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public static async Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic
4444

4545
var document = fixAllContext.Document;
4646
var project = fixAllContext.Project;
47-
var analyzers = GetDiagnosticAnalyzersForContext(fixAllContext);
4847

4948
switch (fixAllContext.Scope)
5049
{
@@ -59,7 +58,7 @@ public static async Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic
5958

6059
case FixAllScope.Project:
6160
projectsToFix = ImmutableArray.Create(project);
62-
allDiagnostics = await GetAllDiagnosticsAsync(fixAllContext, project, analyzers).ConfigureAwait(false);
61+
allDiagnostics = await GetAllDiagnosticsAsync(fixAllContext, project).ConfigureAwait(false);
6362
break;
6463

6564
case FixAllScope.Solution:
@@ -76,7 +75,7 @@ public static async Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic
7675
tasks[i] = Task.Run(
7776
async () =>
7877
{
79-
var projectDiagnostics = await GetAllDiagnosticsAsync(fixAllContext, projectToFix, analyzers).ConfigureAwait(false);
78+
var projectDiagnostics = await GetAllDiagnosticsAsync(fixAllContext, projectToFix).ConfigureAwait(false);
8079
diagnostics.TryAdd(projectToFix.Id, projectDiagnostics);
8180
}, fixAllContext.CancellationToken);
8281
}
@@ -171,44 +170,65 @@ private static ImmutableArray<DiagnosticAnalyzer> GetDiagnosticAnalyzersForConte
171170
.ToImmutableArray();
172171
}
173172

174-
private static async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsAsync(FixAllContext fixAllContext, Project project, ImmutableArray<DiagnosticAnalyzer> analyzers)
173+
/// <summary>
174+
/// Gets all <see cref="Diagnostic"/> instances within a specific <see cref="Project"/> which are relevant to a
175+
/// <see cref="FixAllContext"/>.
176+
/// </summary>
177+
/// <param name="fixAllContext">The context for the Fix All operation.</param>
178+
/// <param name="project">The project.</param>
179+
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation. When the task completes
180+
/// successfully, the <see cref="Task{TResult}.Result"/> will contain the requested diagnostics.</returns>
181+
private static async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsAsync(FixAllContext fixAllContext, Project project)
175182
{
183+
if (GetAnalyzerSyntaxDiagnosticsAsync == null || GetAnalyzerSemanticDiagnosticsAsync == null)
184+
{
185+
return await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false);
186+
}
187+
188+
/*
189+
* The rest of this method is workaround code for issues with Roslyn 1.1...
190+
*/
191+
192+
var analyzers = GetDiagnosticAnalyzersForContext(fixAllContext);
193+
194+
// Most code fixes in this project operate on diagnostics reported by analyzers in this project. However, a
195+
// few code fixes also operate on standard warnings produced by the C# compiler. Special handling is
196+
// required for the latter case since these warnings are not considered "analyzer diagnostics".
176197
bool includeCompilerDiagnostics = fixAllContext.DiagnosticIds.Any(x => x.StartsWith("CS", StringComparison.Ordinal));
198+
199+
// Use a single CompilationWithAnalyzers for the entire operation. This allows us to use the
200+
// GetDeclarationDiagnostics workaround for dotnet/roslyn#7446 a single time, rather than once per document.
177201
var compilation = await project.GetCompilationAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
202+
var compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, fixAllContext.CancellationToken);
203+
compilationWithAnalyzers.Compilation.GetDeclarationDiagnostics(fixAllContext.CancellationToken);
178204

205+
// Note that the following loop to obtain syntax and semantic diagnostics for each document cannot operate
206+
// on parallel due to our use of a single CompilationWithAnalyzers instance. Also note that the following
207+
// code is not sufficient for cases where analyzers register compilation end actions. However, this project
208+
// does not currently contain any such analyzers.
179209
var diagnostics = ImmutableArray<Diagnostic>.Empty;
180-
if (analyzers.Any())
210+
foreach (var document in project.Documents)
181211
{
182-
var compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, fixAllContext.CancellationToken);
183-
if (GetAnalyzerSyntaxDiagnosticsAsync != null && GetAnalyzerSemanticDiagnosticsAsync != null)
184-
{
185-
// This whole block is workaround code for issues with Roslyn 1.1...
186-
compilationWithAnalyzers.Compilation.GetDeclarationDiagnostics(fixAllContext.CancellationToken);
187-
188-
foreach (var document in project.Documents)
189-
{
190-
var syntaxTree = await document.GetSyntaxTreeAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
191-
var syntaxDiagnostics = await GetAnalyzerSyntaxDiagnosticsAsync(compilationWithAnalyzers, syntaxTree, fixAllContext.CancellationToken).ConfigureAwait(false);
192-
diagnostics = diagnostics.AddRange(syntaxDiagnostics);
212+
var syntaxTree = await document.GetSyntaxTreeAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
213+
var syntaxDiagnostics = await GetAnalyzerSyntaxDiagnosticsAsync(compilationWithAnalyzers, syntaxTree, fixAllContext.CancellationToken).ConfigureAwait(false);
214+
diagnostics = diagnostics.AddRange(syntaxDiagnostics);
193215

194-
var semanticModel = await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
195-
var semanticDiagnostics = await GetAnalyzerSemanticDiagnosticsAsync(compilationWithAnalyzers, semanticModel, default(TextSpan?), fixAllContext.CancellationToken).ConfigureAwait(false);
196-
diagnostics = diagnostics.AddRange(semanticDiagnostics);
197-
}
198-
}
199-
else
200-
{
201-
diagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false);
202-
}
216+
var semanticModel = await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
217+
var semanticDiagnostics = await GetAnalyzerSemanticDiagnosticsAsync(compilationWithAnalyzers, semanticModel, default(TextSpan?), fixAllContext.CancellationToken).ConfigureAwait(false);
218+
diagnostics = diagnostics.AddRange(semanticDiagnostics);
203219
}
204220

205221
if (includeCompilerDiagnostics)
206222
{
223+
// This is the special handling for cases where code fixes operate on warnings produced by the C#
224+
// compiler, as opposed to being created by specific analyzers.
207225
var compilerDiagnostics = compilation.GetDiagnostics(fixAllContext.CancellationToken);
208226
diagnostics = diagnostics.AddRange(compilerDiagnostics);
209227
}
210228

211-
diagnostics = diagnostics.Where(x => fixAllContext.DiagnosticIds.Contains(x.Id)).ToImmutableArray();
229+
// Make sure to filter the results to the set requested for the Fix All operation, since analyzers can
230+
// report diagnostics with different IDs.
231+
diagnostics = diagnostics.RemoveAll(x => !fixAllContext.DiagnosticIds.Contains(x.Id));
212232
return diagnostics;
213233
}
214234

0 commit comments

Comments
 (0)