44using System . Collections . Concurrent ;
55using System . Collections . Generic ;
66using System . Collections . Immutable ;
7- using System . Diagnostics ;
87using System . Globalization ;
98using System . IO ;
10- using System . IO . MemoryMappedFiles ;
119using System . Linq ;
12- using System . Runtime . InteropServices ;
13- using System . Threading ;
1410using Humanizer ;
1511using Humanizer . Localisation ;
1612using Microsoft . CodeAnalysis ;
@@ -44,32 +40,6 @@ class DiagnosticsManager
4440 ConcurrentDictionary < string , Diagnostic > Diagnostics
4541 => AppDomainDictionary . Get < ConcurrentDictionary < string , Diagnostic > > ( appDomainDiagnosticsKey . ToString ( ) ) ;
4642
47- /// <summary>
48- /// Attemps to remove a diagnostic for the given product.
49- /// </summary>
50- /// <param name="product">The product diagnostic that might have been pushed previously.</param>
51- /// <returns>The removed diagnostic, or <see langword="null" /> if none was previously pushed.</returns>
52- public void ReportOnce ( Action < Diagnostic > report , string product = Funding . Product )
53- {
54- if ( Diagnostics . TryRemove ( product , out var diagnostic ) &&
55- GetStatus ( diagnostic ) != SponsorStatus . Grace )
56- {
57- // Ensure only one such diagnostic is reported per product for the entire process,
58- // so that we can avoid polluting the error list with duplicates across multiple projects.
59- var id = string . Concat ( SessionId , product , diagnostic . Id ) ;
60- using var mutex = new Mutex ( false , "mutex" + id ) ;
61- mutex . WaitOne ( ) ;
62- using var mmf = CreateOrOpenMemoryMappedFile ( product , 1 ) ;
63- using var accessor = mmf . CreateViewAccessor ( ) ;
64- if ( accessor . ReadByte ( 0 ) == 0 )
65- {
66- accessor . Write ( 0 , ( byte ) 1 ) ;
67- report ( diagnostic ) ;
68- Tracing . Trace ( $ "👈{ diagnostic . Severity . ToString ( ) . ToLowerInvariant ( ) } :{ Process . GetCurrentProcess ( ) . Id } :{ Process . GetCurrentProcess ( ) . ProcessName } :{ product } :{ diagnostic . Id } ") ;
69- }
70- }
71- }
72-
7343 /// <summary>
7444 /// Gets the status of the given product based on a previously stored diagnostic.
7545 /// To ensure the value is always set before returning, use <see cref="GetOrSetStatus"/>.
@@ -100,6 +70,34 @@ public SponsorStatus GetOrSetStatus(ImmutableArray<AdditionalText> manifests, An
10070 public SponsorStatus GetOrSetStatus ( Func < AnalyzerOptions ? > options )
10171 => GetOrSetStatus ( ( ) => options ( ) . GetSponsorAdditionalFiles ( ) , ( ) => options ( ) ? . AnalyzerConfigOptionsProvider . GlobalOptions ) ;
10272
73+ /// <summary>
74+ /// Attemps to remove a diagnostic for the given product.
75+ /// </summary>
76+ /// <param name="product">The product diagnostic that might have been pushed previously.</param>
77+ /// <returns>The removed diagnostic, or <see langword="null" /> if none was previously pushed.</returns>
78+ public Diagnostic ? Pop ( string product = Funding . Product )
79+ {
80+ if ( Diagnostics . TryRemove ( product , out var diagnostic ) &&
81+ GetStatus ( diagnostic ) != SponsorStatus . Grace )
82+ {
83+ return diagnostic ;
84+ }
85+
86+ return null ;
87+ }
88+
89+ /// <summary>
90+ /// Pushes a diagnostic for the given product.
91+ /// </summary>
92+ /// <returns>The same diagnostic that was pushed, for chained invocations.</returns>
93+ Diagnostic Push ( Diagnostic diagnostic , string product = Funding . Product )
94+ {
95+ // We only expect to get one warning per sponsorable+product
96+ // combination, and first one to set wins.
97+ Diagnostics . TryAdd ( product , diagnostic ) ;
98+ return diagnostic ;
99+ }
100+
103101 SponsorStatus GetOrSetStatus ( Func < ImmutableArray < AdditionalText > > getAdditionalFiles , Func < AnalyzerConfigOptions ? > getGlobalOptions )
104102 {
105103 if ( GetStatus ( ) is { } status )
@@ -166,29 +164,6 @@ SponsorStatus GetOrSetStatus(Func<ImmutableArray<AdditionalText>> getAdditionalF
166164 }
167165 }
168166
169- /// <summary>
170- /// Pushes a diagnostic for the given product.
171- /// </summary>
172- /// <returns>The same diagnostic that was pushed, for chained invocations.</returns>
173- Diagnostic Push ( Diagnostic diagnostic , string product = Funding . Product )
174- {
175- // We only expect to get one warning per sponsorable+product
176- // combination, and first one to set wins.
177- if ( Diagnostics . TryAdd ( product , diagnostic ) )
178- {
179- // Reset the process-wide flag for this diagnostic.
180- var id = string . Concat ( SessionId , product , diagnostic . Id ) ;
181- using var mutex = new Mutex ( false , "mutex" + id ) ;
182- mutex . WaitOne ( ) ;
183- using var mmf = CreateOrOpenMemoryMappedFile ( id , 1 ) ;
184- using var accessor = mmf . CreateViewAccessor ( ) ;
185- accessor . Write ( 0 , ( byte ) 0 ) ;
186- Tracing . Trace ( $ "👉{ diagnostic . Severity . ToString ( ) . ToLowerInvariant ( ) } :{ Process . GetCurrentProcess ( ) . Id } :{ Process . GetCurrentProcess ( ) . ProcessName } :{ product } :{ diagnostic . Id } ") ;
187- }
188-
189- return diagnostic ;
190- }
191-
192167 SponsorStatus ? GetStatus ( Diagnostic ? diagnostic ) => diagnostic ? . Properties . TryGetValue ( nameof ( SponsorStatus ) , out var value ) == true
193168 ? value switch
194169 {
@@ -201,19 +176,6 @@ Diagnostic Push(Diagnostic diagnostic, string product = Funding.Product)
201176 }
202177 : null ;
203178
204- static MemoryMappedFile CreateOrOpenMemoryMappedFile ( string mapName , int capacity )
205- {
206- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
207- return MemoryMappedFile . CreateOrOpen ( mapName , capacity ) ;
208-
209- // On *nix, use a file-based memory-mapped file
210- var filePath = $ "/tmp/{ mapName } ";
211- using ( var fs = new FileStream ( filePath , FileMode . OpenOrCreate , FileAccess . ReadWrite , FileShare . ReadWrite ) )
212- fs . Write ( new byte [ capacity ] , 0 , capacity ) ;
213-
214- return MemoryMappedFile . CreateFromFile ( filePath , FileMode . OpenOrCreate ) ;
215- }
216-
217179 internal static DiagnosticDescriptor CreateSponsor ( string [ ] sponsorable , string prefix ) => new (
218180 $ "{ prefix } 100",
219181 Resources . Sponsor_Title ,
0 commit comments