@@ -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}
0 commit comments