9
9
using System . IO ;
10
10
using System . Linq ;
11
11
using System . Reflection ;
12
+ using System . Reflection . Metadata ;
12
13
using System . Threading ;
13
14
using System . Threading . Tasks ;
14
15
using Microsoft . CodeAnalysis . CodeActions ;
15
16
using Microsoft . CodeAnalysis . CodeStyle ;
16
17
using Microsoft . CodeAnalysis . Host ;
17
18
using Microsoft . CodeAnalysis . Host . Mef ;
18
19
using Microsoft . CodeAnalysis . Options ;
20
+ using Microsoft . CodeAnalysis . PooledObjects ;
19
21
using Microsoft . CodeAnalysis . Remote ;
20
22
using Microsoft . CodeAnalysis . Shared . TestHooks ;
21
23
using Microsoft . CodeAnalysis . SolutionCrawler ;
@@ -176,22 +178,47 @@ public async Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsy
176
178
cancellationToken ) . ConfigureAwait ( false ) ;
177
179
}
178
180
179
- private Task < ImmutableArray < DiagnosticData > > ProduceProjectDiagnosticsAsync (
181
+ internal async Task < ImmutableArray < DiagnosticData > > ProduceProjectDiagnosticsAsync (
180
182
Project project ,
181
183
ImmutableArray < DiagnosticAnalyzer > analyzers ,
182
184
ImmutableHashSet < string > ? diagnosticIds ,
183
- IReadOnlyList < DocumentId > documentIds ,
185
+ ImmutableArray < DocumentId > documentIds ,
184
186
bool includeLocalDocumentDiagnostics ,
185
187
bool includeNonLocalDocumentDiagnostics ,
186
188
bool includeProjectNonLocalResult ,
187
189
CancellationToken cancellationToken )
188
190
{
189
- return _incrementalAnalyzer . ProduceProjectDiagnosticsAsync (
191
+ var client = await RemoteHostClient . TryGetClientAsync ( project , cancellationToken ) . ConfigureAwait ( false ) ;
192
+ if ( client is not null )
193
+ {
194
+ var analyzerIds = analyzers . Select ( a => a . GetAnalyzerId ( ) ) . ToImmutableHashSet ( ) ;
195
+ var result = await client . TryInvokeAsync < IRemoteDiagnosticAnalyzerService , ImmutableArray < DiagnosticData > > (
196
+ project ,
197
+ ( service , solution , cancellationToken ) => service . ProduceProjectDiagnosticsAsync (
198
+ solution , project . Id , analyzerIds , diagnosticIds , documentIds ,
199
+ includeLocalDocumentDiagnostics , includeNonLocalDocumentDiagnostics , includeProjectNonLocalResult ,
200
+ cancellationToken ) ,
201
+ cancellationToken ) . ConfigureAwait ( false ) ;
202
+ if ( ! result . HasValue )
203
+ return [ ] ;
204
+
205
+ return result . Value ;
206
+ }
207
+
208
+ // Fallback to proccessing in proc.
209
+ return await _incrementalAnalyzer . ProduceProjectDiagnosticsAsync (
190
210
project , analyzers , diagnosticIds , documentIds ,
191
211
includeLocalDocumentDiagnostics ,
192
212
includeNonLocalDocumentDiagnostics ,
193
213
includeProjectNonLocalResult ,
194
- cancellationToken ) ;
214
+ cancellationToken ) . ConfigureAwait ( false ) ;
215
+ }
216
+
217
+ internal Task < ImmutableArray < DiagnosticAnalyzer > > GetProjectAnalyzersAsync (
218
+ Project project , CancellationToken cancellationToken )
219
+ {
220
+ return _stateManager . GetOrCreateAnalyzersAsync (
221
+ project . Solution . SolutionState , project . State , cancellationToken ) ;
195
222
}
196
223
197
224
private async Task < ImmutableArray < DiagnosticAnalyzer > > GetDiagnosticAnalyzersAsync (
@@ -200,9 +227,7 @@ private async Task<ImmutableArray<DiagnosticAnalyzer>> GetDiagnosticAnalyzersAsy
200
227
Func < DiagnosticAnalyzer , bool > ? shouldIncludeAnalyzer ,
201
228
CancellationToken cancellationToken )
202
229
{
203
- var analyzersForProject = await _stateManager . GetOrCreateAnalyzersAsync (
204
- project . Solution . SolutionState , project . State , cancellationToken ) . ConfigureAwait ( false ) ;
205
-
230
+ var analyzersForProject = await GetProjectAnalyzersAsync ( project , cancellationToken ) . ConfigureAwait ( false ) ;
206
231
var analyzers = analyzersForProject . WhereAsArray ( a => ShouldIncludeAnalyzer ( project , a ) ) ;
207
232
208
233
return analyzers ;
@@ -355,6 +380,59 @@ public async Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescripto
355
380
return project . Solution . SolutionState . Analyzers . GetDiagnosticDescriptorsPerReference ( this . _analyzerInfoCache , project ) ;
356
381
}
357
382
383
+ public async Task < ImmutableArray < DiagnosticAnalyzer > > GetDeprioritizationCandidatesAsync (
384
+ Project project , ImmutableArray < DiagnosticAnalyzer > analyzers , CancellationToken cancellationToken )
385
+ {
386
+ var client = await RemoteHostClient . TryGetClientAsync ( project , cancellationToken ) . ConfigureAwait ( false ) ;
387
+ if ( client is not null )
388
+ {
389
+ var analyzerIds = analyzers . Select ( a => a . GetAnalyzerId ( ) ) . ToImmutableHashSet ( ) ;
390
+ var result = await client . TryInvokeAsync < IRemoteDiagnosticAnalyzerService , ImmutableHashSet < string > > (
391
+ project ,
392
+ ( service , solution , cancellationToken ) => service . GetDeprioritizationCandidatesAsync (
393
+ solution , project . Id , analyzerIds , cancellationToken ) ,
394
+ cancellationToken ) . ConfigureAwait ( false ) ;
395
+ if ( ! result . HasValue )
396
+ return [ ] ;
397
+
398
+ return analyzers . FilterAnalyzers ( result . Value ) ;
399
+ }
400
+
401
+ using var _ = ArrayBuilder < DiagnosticAnalyzer > . GetInstance ( out var builder ) ;
402
+
403
+ var hostAnalyzerInfo = await _stateManager . GetOrCreateHostAnalyzerInfoAsync (
404
+ project . Solution . SolutionState , project . State , cancellationToken ) . ConfigureAwait ( false ) ;
405
+ var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync (
406
+ project , analyzers , hostAnalyzerInfo , this . CrashOnAnalyzerException , cancellationToken ) . ConfigureAwait ( false ) ;
407
+
408
+ foreach ( var analyzer in analyzers )
409
+ {
410
+ if ( await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync ( analyzer ) . ConfigureAwait ( false ) )
411
+ builder . Add ( analyzer ) ;
412
+ }
413
+
414
+ return builder . ToImmutableAndClear ( ) ;
415
+
416
+ async Task < bool > IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync ( DiagnosticAnalyzer analyzer )
417
+ {
418
+ // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket,
419
+ // as these are computationally more expensive.
420
+ // Note that we never de-prioritize compiler analyzer, even though it registers a SemanticModel action.
421
+ if ( compilationWithAnalyzers == null ||
422
+ analyzer . IsWorkspaceDiagnosticAnalyzer ( ) ||
423
+ analyzer . IsCompilerAnalyzer ( ) )
424
+ {
425
+ return false ;
426
+ }
427
+
428
+ var telemetryInfo = await compilationWithAnalyzers . GetAnalyzerTelemetryInfoAsync ( analyzer , cancellationToken ) . ConfigureAwait ( false ) ;
429
+ if ( telemetryInfo == null )
430
+ return false ;
431
+
432
+ return telemetryInfo . SymbolStartActionsCount > 0 || telemetryInfo . SemanticModelActionsCount > 0 ;
433
+ }
434
+ }
435
+
358
436
private sealed class DiagnosticAnalyzerComparer : IEqualityComparer < DiagnosticAnalyzer >
359
437
{
360
438
public static readonly DiagnosticAnalyzerComparer Instance = new ( ) ;
0 commit comments