@@ -666,7 +666,7 @@ async Task<bool> checkHelpLinkAsync(string helpLink)
666666
667667 async Task < bool > createGlobalConfigFilesAsync ( )
668668 {
669- using var shippedFilesDataBuilder = ArrayBuilder < ReleaseTrackingData > . GetInstance ( ) ;
669+ using var releaseTrackingFilesDataBuilder = ArrayBuilder < ReleaseTrackingData > . GetInstance ( ) ;
670670 using var versionsBuilder = PooledHashSet < Version > . GetInstance ( ) ;
671671
672672 // Validate all assemblies exist on disk and can be loaded.
@@ -705,7 +705,19 @@ async Task<bool> createGlobalConfigFilesAsync()
705705
706706 var assemblyName = Path . GetFileNameWithoutExtension ( assembly ) ;
707707 var shippedFile = Path . Combine ( assemblyDir , "AnalyzerReleases" , assemblyName , ReleaseTrackingHelper . ShippedFileName ) ;
708- if ( File . Exists ( shippedFile ) )
708+ var unshippedFile = Path . Combine ( assemblyDir , "AnalyzerReleases" , assemblyName , ReleaseTrackingHelper . UnshippedFileName ) ;
709+ var shippedFileExists = File . Exists ( shippedFile ) ;
710+ var unshippedFileExists = File . Exists ( unshippedFile ) ;
711+
712+ if ( shippedFileExists ^ unshippedFileExists )
713+ {
714+ var existingFile = shippedFileExists ? shippedFile : unshippedFile ;
715+ var nonExistingFile = shippedFileExists ? unshippedFile : shippedFile ;
716+ await Console . Error . WriteLineAsync ( $ "Expected both '{ shippedFile } ' and '{ unshippedFile } ' to exist or not exist, but '{ existingFile } ' exists and '{ nonExistingFile } ' does not exist.") . ConfigureAwait ( false ) ;
717+ return false ;
718+ }
719+
720+ if ( shippedFileExists )
709721 {
710722 sawShippedFile = true ;
711723
@@ -717,14 +729,24 @@ async Task<bool> createGlobalConfigFilesAsync()
717729
718730 try
719731 {
732+ // Read shipped file
720733 using var fileStream = File . OpenRead ( shippedFile ) ;
721734 var sourceText = SourceText . From ( fileStream ) ;
722735 var releaseTrackingData = ReleaseTrackingHelper . ReadReleaseTrackingData ( shippedFile , sourceText ,
723- onDuplicateEntryInRelease : ( _1 , _2 , _3 , _4 , line ) => throw new Exception ( $ "Duplicate entry in { shippedFile } at { line . LineNumber } : '{ line } '") ,
724- onInvalidEntry : ( line , _2 , _3 , _4 ) => throw new Exception ( $ "Invalid entry in { shippedFile } at { line . LineNumber } : '{ line } '") ,
736+ onDuplicateEntryInRelease : ( _1 , _2 , _3 , _4 , line ) => throw new InvalidOperationException ( $ "Duplicate entry in { shippedFile } at { line . LineNumber } : '{ line } '") ,
737+ onInvalidEntry : ( line , _2 , _3 , _4 ) => throw new InvalidOperationException ( $ "Invalid entry in { shippedFile } at { line . LineNumber } : '{ line } '") ,
725738 isShippedFile : true ) ;
726- shippedFilesDataBuilder . Add ( releaseTrackingData ) ;
739+ releaseTrackingFilesDataBuilder . Add ( releaseTrackingData ) ;
727740 versionsBuilder . AddRange ( releaseTrackingData . Versions ) ;
741+
742+ // Read unshipped file
743+ using var fileStreamUnshipped = File . OpenRead ( unshippedFile ) ;
744+ var sourceTextUnshipped = SourceText . From ( fileStreamUnshipped ) ;
745+ var releaseTrackingDataUnshipped = ReleaseTrackingHelper . ReadReleaseTrackingData ( unshippedFile , sourceTextUnshipped ,
746+ onDuplicateEntryInRelease : ( _1 , _2 , _3 , _4 , line ) => throw new InvalidOperationException ( $ "Duplicate entry in { unshippedFile } at { line . LineNumber } : '{ line } '") ,
747+ onInvalidEntry : ( line , _2 , _3 , _4 ) => throw new InvalidOperationException ( $ "Invalid entry in { unshippedFile } at { line . LineNumber } : '{ line } '") ,
748+ isShippedFile : false ) ;
749+ releaseTrackingFilesDataBuilder . Add ( releaseTrackingDataUnshipped ) ;
728750 }
729751#pragma warning disable CA1031 // Do not catch general exception types
730752 catch ( Exception ex )
@@ -744,33 +766,49 @@ async Task<bool> createGlobalConfigFilesAsync()
744766
745767 if ( versionsBuilder . Count > 0 )
746768 {
747- var shippedFilesData = shippedFilesDataBuilder . ToImmutable ( ) ;
769+ var releaseTrackingData = releaseTrackingFilesDataBuilder . ToImmutableArray ( ) ;
748770
749- // Generate global analyzer config files for each shipped version, if required .
771+ // Generate global analyzer config files for each shipped version.
750772 foreach ( var version in versionsBuilder )
751773 {
752- var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix ( version ) ;
753-
754- foreach ( var analysisMode in Enum . GetValues ( typeof ( AnalysisMode ) ) )
755- {
756- CreateGlobalConfig ( version , analysisLevelVersionString , ( AnalysisMode ) analysisMode ! , shippedFilesData , category : null ) ;
757- foreach ( var category in categories )
758- {
759- CreateGlobalConfig ( version , analysisLevelVersionString , ( AnalysisMode ) analysisMode ! , shippedFilesData , category ) ;
760- }
761- }
774+ CreateGlobalConfigsForVersion ( version , isShippedVersion : true , releaseTrackingData ) ;
762775 }
776+
777+ // Generate global analyzer config files for unshipped version.
778+ // See https://github.com/dotnet/roslyn-analyzers/issues/6247 for details.
779+
780+ // Use 'unshippedVersion = maxShippedVersion + 1' for unshipped data.
781+ var maxShippedVersion = versionsBuilder . Max ( ) ;
782+ var unshippedVersion = new Version ( maxShippedVersion ! . Major + 1 , maxShippedVersion . Minor ) ;
783+ CreateGlobalConfigsForVersion ( unshippedVersion , isShippedVersion : false , releaseTrackingData ) ;
763784 }
764785
765786 return true ;
766787
767788 // Local functions.
789+ void CreateGlobalConfigsForVersion (
790+ Version version ,
791+ bool isShippedVersion ,
792+ ImmutableArray < ReleaseTrackingData > releaseTrackingData )
793+ {
794+ var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix ( version ) ;
795+
796+ foreach ( var analysisMode in Enum . GetValues ( typeof ( AnalysisMode ) ) )
797+ {
798+ CreateGlobalConfig ( version , isShippedVersion , analysisLevelVersionString , ( AnalysisMode ) analysisMode ! , releaseTrackingData , category : null ) ;
799+ foreach ( var category in categories ! )
800+ {
801+ CreateGlobalConfig ( version , isShippedVersion , analysisLevelVersionString , ( AnalysisMode ) analysisMode ! , releaseTrackingData , category ) ;
802+ }
803+ }
804+ }
768805
769806 void CreateGlobalConfig (
770807 Version version ,
808+ bool isShippedVersion ,
771809 string analysisLevelVersionString ,
772810 AnalysisMode analysisMode ,
773- ImmutableArray < ReleaseTrackingData > shippedFilesData ,
811+ ImmutableArray < ReleaseTrackingData > releaseTrackingData ,
774812 string ? category )
775813 {
776814 var analysisLevelPropName = "AnalysisLevel" ;
@@ -793,7 +831,7 @@ void CreateGlobalConfig(
793831 analysisMode ,
794832 category ,
795833 allRulesById ,
796- ( shippedFilesData , version ) ) ;
834+ ( releaseTrackingData , version , isShippedVersion ) ) ;
797835 }
798836
799837 static string GetNormalizedVersionStringForEditorconfigFileNameSuffix ( Version version )
@@ -1125,7 +1163,7 @@ private static void CreateGlobalconfig(
11251163 AnalysisMode analysisMode ,
11261164 string ? category ,
11271165 SortedList < string , DiagnosticDescriptor > sortedRulesById ,
1128- ( ImmutableArray < ReleaseTrackingData > shippedFiles , Version version ) shippedReleaseData )
1166+ ( ImmutableArray < ReleaseTrackingData > releaseTrackingData , Version version , bool isShippedVersion ) releaseTrackingDataAndVersion )
11291167 {
11301168 Debug . Assert ( editorconfigFileName . EndsWith ( ".editorconfig" , StringComparison . Ordinal ) ) ;
11311169
@@ -1135,7 +1173,7 @@ private static void CreateGlobalconfig(
11351173 analysisMode ,
11361174 category ,
11371175 sortedRulesById ,
1138- shippedReleaseData ) ;
1176+ releaseTrackingDataAndVersion ) ;
11391177 var directory = Directory . CreateDirectory ( folder ) ;
11401178 var editorconfigFilePath = Path . Combine ( directory . FullName , editorconfigFileName . ToLowerInvariant ( ) ) ;
11411179 File . WriteAllText ( editorconfigFilePath , text ) ;
@@ -1148,7 +1186,7 @@ static string GetGlobalconfigText(
11481186 AnalysisMode analysisMode ,
11491187 string ? category ,
11501188 SortedList < string , DiagnosticDescriptor > sortedRulesById ,
1151- ( ImmutableArray < ReleaseTrackingData > shippedFiles , Version version ) ? shippedReleaseData )
1189+ ( ImmutableArray < ReleaseTrackingData > releaseTrackingData , Version version , bool isShippedVersion ) ? releaseTrackingDataAndVersion )
11521190 {
11531191 var result = new StringBuilder ( ) ;
11541192 StartGlobalconfig ( ) ;
@@ -1257,14 +1295,16 @@ bool AddRule(DiagnosticDescriptor rule, string? category)
12571295 effectiveSeverity = DiagnosticSeverity . Warning ;
12581296 }
12591297
1260- if ( shippedReleaseData != null )
1298+ if ( releaseTrackingDataAndVersion != null )
12611299 {
12621300 isEnabledByDefault = isEnabledRuleForNonDefaultAnalysisMode ;
1263- var maxVersion = shippedReleaseData . Value . version ;
1301+ var maxVersion = releaseTrackingDataAndVersion . Value . isShippedVersion ?
1302+ releaseTrackingDataAndVersion . Value . version :
1303+ ReleaseTrackingHelper . UnshippedVersion ;
12641304 var foundReleaseTrackingEntry = false ;
1265- foreach ( var shippedFile in shippedReleaseData . Value . shippedFiles )
1305+ foreach ( var releaseTrackingData in releaseTrackingDataAndVersion . Value . releaseTrackingData )
12661306 {
1267- if ( shippedFile . TryGetLatestReleaseTrackingLine ( rule . Id , maxVersion , out _ , out var releaseTrackingLine ) )
1307+ if ( releaseTrackingData . TryGetLatestReleaseTrackingLine ( rule . Id , maxVersion , out _ , out var releaseTrackingLine ) )
12681308 {
12691309 foundReleaseTrackingEntry = true ;
12701310
0 commit comments