Skip to content

Commit e17ea25

Browse files
committed
Merge pull request #2017 from sharwell/apply-fixes
Add /apply option to write /fixall changes back to disk
2 parents 6f4d1e9 + aa0acfd commit e17ea25

2 files changed

Lines changed: 55 additions & 14 deletions

File tree

StyleCop.Analyzers/StyleCopTester/Program.cs

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
7474
}
7575
else
7676
{
77+
bool applyChanges = args.Contains("/apply");
78+
if (applyChanges)
79+
{
80+
if (!args.Contains("/fixall"))
81+
{
82+
Console.Error.WriteLine("Error: /apply can only be used with /fixall");
83+
return;
84+
}
85+
}
86+
7787
Stopwatch stopwatch = Stopwatch.StartNew();
7888
var analyzers = GetAllAnalyzers();
7989

@@ -91,7 +101,7 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
91101

92102
Console.WriteLine($"Loaded solution in {stopwatch.ElapsedMilliseconds}ms");
93103

94-
if (!args.Contains("/nostats"))
104+
if (args.Contains("/stats"))
95105
{
96106
List<Project> csharpProjects = solution.Projects.Where(i => i.Language == LanguageNames.CSharp).ToList();
97107

@@ -107,7 +117,9 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
107117

108118
stopwatch.Restart();
109119

110-
var diagnostics = await GetAnalyzerDiagnosticsAsync(solution, solutionPath, analyzers, cancellationToken).ConfigureAwait(true);
120+
bool force = args.Contains("/force");
121+
122+
var diagnostics = await GetAnalyzerDiagnosticsAsync(solution, solutionPath, analyzers, force, cancellationToken).ConfigureAwait(true);
111123
var allDiagnostics = diagnostics.SelectMany(i => i.Value).ToImmutableArray();
112124

113125
Console.WriteLine($"Found {allDiagnostics.Length} diagnostics in {stopwatch.ElapsedMilliseconds}ms");
@@ -140,7 +152,7 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
140152

141153
if (args.Contains("/fixall"))
142154
{
143-
await TestFixAllAsync(stopwatch, solution, diagnostics, cancellationToken).ConfigureAwait(true);
155+
await TestFixAllAsync(stopwatch, solution, diagnostics, applyChanges, cancellationToken).ConfigureAwait(true);
144156
}
145157
}
146158
}
@@ -177,7 +189,7 @@ private static void WriteDiagnosticResults(ImmutableArray<Tuple<ProjectId, Diagn
177189
File.WriteAllText(uniqueFileName, uniqueOutput.ToString(), Encoding.UTF8);
178190
}
179191

180-
private static async Task TestFixAllAsync(Stopwatch stopwatch, Solution solution, ImmutableDictionary<ProjectId, ImmutableArray<Diagnostic>> diagnostics, CancellationToken cancellationToken)
192+
private static async Task TestFixAllAsync(Stopwatch stopwatch, Solution solution, ImmutableDictionary<ProjectId, ImmutableArray<Diagnostic>> diagnostics, bool applyChanges, CancellationToken cancellationToken)
181193
{
182194
Console.WriteLine("Calculating fixes");
183195

@@ -191,6 +203,11 @@ private static async Task TestFixAllAsync(Stopwatch stopwatch, Solution solution
191203
}
192204

193205
Console.WriteLine($"Found {equivalenceGroups.Count} equivalence groups.");
206+
if (applyChanges && equivalenceGroups.Count > 1)
207+
{
208+
Console.Error.WriteLine("/apply can only be used with a single equivalence group.");
209+
return;
210+
}
194211

195212
Console.WriteLine("Calculating changes");
196213

@@ -200,7 +217,24 @@ private static async Task TestFixAllAsync(Stopwatch stopwatch, Solution solution
200217
{
201218
stopwatch.Restart();
202219
Console.WriteLine($"Calculating fix for {fix.CodeFixEquivalenceKey} using {fix.FixAllProvider} for {fix.NumberOfDiagnostics} instances.");
203-
await fix.GetOperationsAsync(cancellationToken).ConfigureAwait(true);
220+
var operations = await fix.GetOperationsAsync(cancellationToken).ConfigureAwait(true);
221+
if (applyChanges)
222+
{
223+
var applyOperations = operations.OfType<ApplyChangesOperation>().ToList();
224+
if (applyOperations.Count > 1)
225+
{
226+
Console.Error.WriteLine("/apply can only apply a single code action operation.");
227+
}
228+
else if (applyOperations.Count == 0)
229+
{
230+
Console.WriteLine("No changes were found to apply.");
231+
}
232+
else
233+
{
234+
applyOperations[0].Apply(solution.Workspace, cancellationToken);
235+
}
236+
}
237+
204238
WriteLine($"Calculating changes completed in {stopwatch.ElapsedMilliseconds}ms. This is {fix.NumberOfDiagnostics / stopwatch.Elapsed.TotalSeconds:0.000} instances/second.", ConsoleColor.Yellow);
205239
}
206240
catch (Exception ex)
@@ -389,7 +423,7 @@ private static ImmutableDictionary<FixAllProvider, ImmutableHashSet<string>> Get
389423
return fixAllProviders.ToImmutableDictionary();
390424
}
391425

392-
private static async Task<ImmutableDictionary<ProjectId, ImmutableArray<Diagnostic>>> GetAnalyzerDiagnosticsAsync(Solution solution, string solutionPath, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
426+
private static async Task<ImmutableDictionary<ProjectId, ImmutableArray<Diagnostic>>> GetAnalyzerDiagnosticsAsync(Solution solution, string solutionPath, ImmutableArray<DiagnosticAnalyzer> analyzers, bool force, CancellationToken cancellationToken)
393427
{
394428
List<KeyValuePair<ProjectId, Task<ImmutableArray<Diagnostic>>>> projectDiagnosticTasks = new List<KeyValuePair<ProjectId, Task<ImmutableArray<Diagnostic>>>>();
395429

@@ -401,7 +435,7 @@ private static async Task<ImmutableDictionary<ProjectId, ImmutableArray<Diagnost
401435
continue;
402436
}
403437

404-
projectDiagnosticTasks.Add(new KeyValuePair<ProjectId, Task<ImmutableArray<Diagnostic>>>(project.Id, GetProjectAnalyzerDiagnosticsAsync(analyzers, project, cancellationToken)));
438+
projectDiagnosticTasks.Add(new KeyValuePair<ProjectId, Task<ImmutableArray<Diagnostic>>>(project.Id, GetProjectAnalyzerDiagnosticsAsync(analyzers, project, force, cancellationToken)));
405439
}
406440

407441
ImmutableDictionary<ProjectId, ImmutableArray<Diagnostic>>.Builder projectDiagnosticBuilder = ImmutableDictionary.CreateBuilder<ProjectId, ImmutableArray<Diagnostic>>();
@@ -418,17 +452,22 @@ private static async Task<ImmutableDictionary<ProjectId, ImmutableArray<Diagnost
418452
/// </summary>
419453
/// <param name="analyzers">The list of analyzers that should be used</param>
420454
/// <param name="project">The project that should be analyzed</param>
455+
/// <param name="force"><see langword="true"/> to force the analyzers to be enabled; otherwise,
456+
/// <see langword="false"/> to use the behavior configured for the specified <paramref name="project"/>.</param>
421457
/// <param name="cancellationToken">The cancellation token that the task will observe.</param>
422458
/// <returns>A list of diagnostics inside the project</returns>
423-
private static async Task<ImmutableArray<Diagnostic>> GetProjectAnalyzerDiagnosticsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, Project project, CancellationToken cancellationToken)
459+
private static async Task<ImmutableArray<Diagnostic>> GetProjectAnalyzerDiagnosticsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, Project project, bool force, CancellationToken cancellationToken)
424460
{
425461
var supportedDiagnosticsSpecificOptions = new Dictionary<string, ReportDiagnostic>();
426-
foreach (var analyzer in analyzers)
462+
if (force)
427463
{
428-
foreach (var diagnostic in analyzer.SupportedDiagnostics)
464+
foreach (var analyzer in analyzers)
429465
{
430-
// make sure the analyzers we are testing are enabled
431-
supportedDiagnosticsSpecificOptions[diagnostic.Id] = ReportDiagnostic.Default;
466+
foreach (var diagnostic in analyzer.SupportedDiagnostics)
467+
{
468+
// make sure the analyzers we are testing are enabled
469+
supportedDiagnosticsSpecificOptions[diagnostic.Id] = ReportDiagnostic.Default;
470+
}
432471
}
433472
}
434473

@@ -452,10 +491,12 @@ private static void PrintHelp()
452491
Console.WriteLine("Usage: StyleCopTester [options] <Solution>");
453492
Console.WriteLine("Options:");
454493
Console.WriteLine("/all Run all StyleCopAnalyzers analyzers, including ones that are disabled by default");
455-
Console.WriteLine("/nostats Disable the display of statistics");
494+
Console.WriteLine("/stats Display statistics of the solution");
456495
Console.WriteLine("/codefixes Test single code fixes");
457496
Console.WriteLine("/fixall Test fix all providers");
458497
Console.WriteLine("/id:<id> Enable analyzer with diagnostic ID < id > (when this is specified, only this analyzer is enabled)");
498+
Console.WriteLine("/apply Write code fix changes back to disk");
499+
Console.WriteLine("/force Force an analyzer to be enabled, regardless of the configured rule set(s) for the solution");
459500
}
460501
}
461502
}

StyleCop.Analyzers/StyleCopTester/StyleCopTester.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
<CodeAnalysisRuleSet>..\StyleCop.Analyzers.Internal.ruleset</CodeAnalysisRuleSet>
3838
</PropertyGroup>
3939
<PropertyGroup>
40-
<StartArguments>..\..\StyleCopAnalyzers.sln</StartArguments>
40+
<StartArguments>..\..\StyleCopAnalyzers.sln /stats</StartArguments>
4141
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
4242
</PropertyGroup>
4343
<PropertyGroup>

0 commit comments

Comments
 (0)