@@ -113,9 +113,9 @@ public string BatchFixedCode
113113
114114 public List < string > BatchFixedSources { get ; } = new List < string > ( ) ;
115115
116- public int NumberOfIncrementalIterations { get ; set ; } = DefaultNumberOfIncrementalIterations ;
116+ public int ? NumberOfIncrementalIterations { get ; set ; }
117117
118- public int NumberOfFixAllIterations { get ; set ; } = 1 ;
118+ public int ? NumberOfFixAllIterations { get ; set ; }
119119
120120 public bool AllowNewCompilerDiagnostics { get ; set ; } = false ;
121121
@@ -722,7 +722,41 @@ private static string FormatDiagnostics(ImmutableArray<DiagnosticAnalyzer> analy
722722 var newSources = this . FixedSources . ToArray ( ) ;
723723 var batchNewSources = this . BatchFixedSources . Any ( ) ? this . BatchFixedSources . ToArray ( ) : newSources ;
724724
725- var t1 = this . VerifyFixInternalAsync ( this . Language , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , newSources , oldFileNames , newFileNames , this . NumberOfIncrementalIterations , FixEachAnalyzerDiagnosticAsync , cancellationToken ) . ConfigureAwait ( false ) ;
725+ int numberOfIncrementalIterations ;
726+ int numberOfFixAllIterations ;
727+ if ( this . NumberOfIncrementalIterations != null )
728+ {
729+ numberOfIncrementalIterations = this . NumberOfIncrementalIterations . Value ;
730+ }
731+ else
732+ {
733+ if ( ! HasAnyChange ( oldSources , newSources , oldFileNames , newFileNames ) )
734+ {
735+ numberOfIncrementalIterations = 0 ;
736+ }
737+ else
738+ {
739+ numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations ;
740+ }
741+ }
742+
743+ if ( this . NumberOfFixAllIterations != null )
744+ {
745+ numberOfFixAllIterations = this . NumberOfFixAllIterations . Value ;
746+ }
747+ else
748+ {
749+ if ( ! HasAnyChange ( oldSources , batchNewSources , oldFileNames , newFileNames ) )
750+ {
751+ numberOfFixAllIterations = 0 ;
752+ }
753+ else
754+ {
755+ numberOfFixAllIterations = 1 ;
756+ }
757+ }
758+
759+ var t1 = this . VerifyFixInternalAsync ( this . Language , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , newSources , oldFileNames , newFileNames , numberOfIncrementalIterations , FixEachAnalyzerDiagnosticAsync , cancellationToken ) . ConfigureAwait ( false ) ;
726760
727761 var fixAllProvider = this . GetCodeFixProviders ( ) . Select ( codeFixProvider => codeFixProvider . GetFixAllProvider ( ) ) . Where ( codeFixProvider => codeFixProvider != null ) . ToImmutableArray ( ) ;
728762
@@ -737,19 +771,19 @@ private static string FormatDiagnostics(ImmutableArray<DiagnosticAnalyzer> analy
737771 await t1 ;
738772 }
739773
740- var t2 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , this . NumberOfFixAllIterations , FixAllAnalyzerDiagnosticsInDocumentAsync , cancellationToken ) . ConfigureAwait ( false ) ;
774+ var t2 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , numberOfFixAllIterations , FixAllAnalyzerDiagnosticsInDocumentAsync , cancellationToken ) . ConfigureAwait ( false ) ;
741775 if ( Debugger . IsAttached )
742776 {
743777 await t2 ;
744778 }
745779
746- var t3 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , this . NumberOfFixAllIterations , FixAllAnalyzerDiagnosticsInProjectAsync , cancellationToken ) . ConfigureAwait ( false ) ;
780+ var t3 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , numberOfFixAllIterations , FixAllAnalyzerDiagnosticsInProjectAsync , cancellationToken ) . ConfigureAwait ( false ) ;
747781 if ( Debugger . IsAttached )
748782 {
749783 await t3 ;
750784 }
751785
752- var t4 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , this . NumberOfFixAllIterations , FixAllAnalyzerDiagnosticsInSolutionAsync , cancellationToken ) . ConfigureAwait ( false ) ;
786+ var t4 = this . VerifyFixInternalAsync ( LanguageNames . CSharp , this . GetDiagnosticAnalyzers ( ) . ToImmutableArray ( ) , this . GetCodeFixProviders ( ) . ToImmutableArray ( ) , oldSources , batchNewSources ?? newSources , oldFileNames , newFileNames , numberOfFixAllIterations , FixAllAnalyzerDiagnosticsInSolutionAsync , cancellationToken ) . ConfigureAwait ( false ) ;
753787 if ( Debugger . IsAttached )
754788 {
755789 await t4 ;
@@ -834,6 +868,21 @@ private async Task VerifyFixInternalAsync(
834868 }
835869 }
836870
871+ private static bool HasAnyChange ( string [ ] oldSources , string [ ] newSources , string [ ] oldFileNames , string [ ] newFileNames )
872+ {
873+ if ( ! oldSources . SequenceEqual ( newSources ) )
874+ {
875+ return true ;
876+ }
877+
878+ if ( oldFileNames != null && newFileNames != null && ! oldFileNames . SequenceEqual ( newFileNames ) )
879+ {
880+ return true ;
881+ }
882+
883+ return false ;
884+ }
885+
837886 private static async Task < Project > FixEachAnalyzerDiagnosticAsync ( ImmutableArray < DiagnosticAnalyzer > analyzers , ImmutableArray < CodeFixProvider > codeFixProviders , int ? codeFixIndex , Project project , int numberOfIterations , CancellationToken cancellationToken )
838887 {
839888 var codeFixProvider = codeFixProviders . Single ( ) ;
@@ -860,14 +909,15 @@ private static async Task<Project> FixEachAnalyzerDiagnosticAsync(ImmutableArray
860909 break ;
861910 }
862911
863- if ( -- numberOfIterations < 0 )
912+ if ( -- numberOfIterations < - 1 )
864913 {
865914 Assert . True ( false , "The upper limit for the number of code fix iterations was exceeded" ) ;
866915 }
867916
868917 previousDiagnostics = analyzerDiagnostics ;
869918
870919 done = true ;
920+ bool anyActions = false ;
871921 foreach ( var diagnostic in analyzerDiagnostics )
872922 {
873923 if ( ! codeFixProvider . FixableDiagnosticIds . Contains ( diagnostic . Id ) )
@@ -882,6 +932,8 @@ private static async Task<Project> FixEachAnalyzerDiagnosticAsync(ImmutableArray
882932
883933 if ( actions . Count > 0 )
884934 {
935+ anyActions = true ;
936+
885937 var fixedProject = await ApplyFixAsync ( project , actions . ElementAt ( codeFixIndex . GetValueOrDefault ( 0 ) ) , cancellationToken ) . ConfigureAwait ( false ) ;
886938 if ( fixedProject != project )
887939 {
@@ -892,6 +944,14 @@ private static async Task<Project> FixEachAnalyzerDiagnosticAsync(ImmutableArray
892944 }
893945 }
894946 }
947+
948+ if ( ! anyActions )
949+ {
950+ Assert . True ( done ) ;
951+
952+ // Avoid counting iterations that do not provide any code actions
953+ numberOfIterations ++ ;
954+ }
895955 }
896956 while ( ! done ) ;
897957
@@ -951,7 +1011,7 @@ private static async Task<Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllSc
9511011 break ;
9521012 }
9531013
954- if ( -- numberOfIterations < 0 )
1014+ if ( -- numberOfIterations < - 1 )
9551015 {
9561016 Assert . True ( false , "The upper limit for the number of fix all iterations was exceeded" ) ;
9571017 }
@@ -979,7 +1039,8 @@ private static async Task<Project> FixAllAnalyerDiagnosticsInScopeAsync(FixAllSc
9791039
9801040 if ( firstDiagnostic == null )
9811041 {
982- return project ;
1042+ numberOfIterations ++ ;
1043+ break ;
9831044 }
9841045
9851046 previousDiagnostics = analyzerDiagnostics ;
0 commit comments