-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathCompilationWithAnalyzers.cs
1399 lines (1208 loc) · 74.1 KB
/
CompilationWithAnalyzers.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver;
namespace Microsoft.CodeAnalysis.Diagnostics
{
public class CompilationWithAnalyzers
{
private readonly Compilation _compilation;
private readonly CompilationData _compilationData;
private readonly ImmutableArray<DiagnosticAnalyzer> _analyzers;
private readonly CompilationWithAnalyzersOptions _analysisOptions;
private readonly CancellationToken _cancellationToken;
private readonly AnalyzerManager _analyzerManager;
/// <summary>
/// Pool of <see cref="AnalyzerDriver"/>s used for analyzer execution.
/// </summary>
private readonly ObjectPool<AnalyzerDriver> _driverPool;
/// <summary>
/// Contains the partial analysis state per-analyzer. It tracks:
/// 1. Global set of pending compilation events. This is used to populate the event queue for analyzer execution.
/// 2. Per-analyzer set of pending compilation events, symbols, declarations, etc. Each of these pending entities has a <see cref="AnalysisState.AnalyzerStateData"/> state object to track partial analysis.
/// </summary>
private readonly AnalysisState _analysisState;
/// <summary>
/// Builder for storing current, possibly partial, analysis results:
/// 1. Diagnostics reported by analyzers.
/// 2. AnalyzerTelemetryInfo.
/// </summary>
private readonly AnalysisResultBuilder _analysisResultBuilder;
/// <summary>
/// Set of exception diagnostics reported for exceptions thrown by the analyzers.
/// </summary>
private readonly ConcurrentSet<Diagnostic> _exceptionDiagnostics = new ConcurrentSet<Diagnostic>();
/// <summary>
/// Lock to track the set of active tasks computing tree diagnostics and task computing compilation diagnostics.
/// </summary>
private readonly object _executingTasksLock = new object();
private readonly Dictionary<SourceOrAdditionalFile, Tuple<Task, CancellationTokenSource>>? _executingConcurrentTreeTasksOpt;
private Tuple<Task, CancellationTokenSource>? _executingCompilationOrNonConcurrentTreeTask;
/// <summary>
/// Used to generate a unique token for each tree diagnostics request.
/// The token is used to determine the priority of each request.
/// Each new tree diagnostic request gets an incremented token value and has higher priority over other requests for the same tree.
/// Compilation diagnostics requests always have the lowest priority.
/// </summary>
private int _currentToken = 0;
/// <summary>
/// Map from active tasks computing tree diagnostics to their token number.
/// </summary>
private readonly Dictionary<Task, int>? _concurrentTreeTaskTokensOpt;
/// <summary>
/// Pool of event queues to serve each diagnostics request.
/// </summary>
private readonly ObjectPool<AsyncQueue<CompilationEvent>> _eventQueuePool = new ObjectPool<AsyncQueue<CompilationEvent>>(() => new AsyncQueue<CompilationEvent>());
private static readonly AsyncQueue<CompilationEvent> s_EmptyEventQueue = new AsyncQueue<CompilationEvent>();
/// <summary>
/// Underlying <see cref="Compilation"/> with a non-null <see cref="Compilation.EventQueue"/>, used to drive analyzer execution.
/// </summary>
public Compilation Compilation => _compilation;
/// <summary>
/// Analyzers to execute on the compilation.
/// </summary>
public ImmutableArray<DiagnosticAnalyzer> Analyzers => _analyzers;
/// <summary>
/// Options to configure analyzer execution.
/// </summary>
public CompilationWithAnalyzersOptions AnalysisOptions => _analysisOptions;
/// <summary>
/// An optional cancellation token which can be used to cancel analysis.
/// Note: This token is only used if the API invoked to get diagnostics doesn't provide a cancellation token.
/// </summary>
public CancellationToken CancellationToken => _cancellationToken;
/// <summary>
/// Creates a new compilation by attaching diagnostic analyzers to an existing compilation.
/// </summary>
/// <param name="compilation">The original compilation.</param>
/// <param name="analyzers">The set of analyzers to include in future analyses.</param>
/// <param name="options">Options that are passed to analyzers.</param>
/// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions? options, CancellationToken cancellationToken)
: this(compilation, analyzers, new CompilationWithAnalyzersOptions(options, onAnalyzerException: null, analyzerExceptionFilter: null, concurrentAnalysis: true, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: false), cancellationToken)
{
}
/// <summary>
/// Creates a new compilation by attaching diagnostic analyzers to an existing compilation.
/// </summary>
/// <param name="compilation">The original compilation.</param>
/// <param name="analyzers">The set of analyzers to include in future analyses.</param>
/// <param name="analysisOptions">Options to configure analyzer execution.</param>
public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions)
: this(compilation, analyzers, analysisOptions, cancellationToken: CancellationToken.None)
{
}
private CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions, CancellationToken cancellationToken)
{
VerifyArguments(compilation, analyzers, analysisOptions);
compilation = compilation
.WithOptions(compilation.Options.WithReportSuppressedDiagnostics(analysisOptions.ReportSuppressedDiagnostics))
.WithSemanticModelProvider(new CachingSemanticModelProvider())
.WithEventQueue(new AsyncQueue<CompilationEvent>());
_compilation = compilation;
_analyzers = analyzers;
_analysisOptions = analysisOptions;
_cancellationToken = cancellationToken;
_compilationData = new CompilationData(_compilation);
_analysisState = new AnalysisState(analyzers, _compilationData.SemanticModelProvider, _compilation.Options);
_analysisResultBuilder = new AnalysisResultBuilder(analysisOptions.LogAnalyzerExecutionTime, analyzers, _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray<AdditionalText>.Empty);
_analyzerManager = new AnalyzerManager(analyzers);
_driverPool = new ObjectPool<AnalyzerDriver>(() => _compilation.CreateAnalyzerDriver(analyzers, _analyzerManager, severityFilter: SeverityFilter.None));
_executingConcurrentTreeTasksOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary<SourceOrAdditionalFile, Tuple<Task, CancellationTokenSource>>() : null;
_concurrentTreeTaskTokensOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary<Task, int>() : null;
_executingCompilationOrNonConcurrentTreeTask = null;
}
#region Helper methods for public API argument validation
private static void VerifyArguments(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilationWithAnalyzersOptions analysisOptions)
{
if (compilation == null)
{
throw new ArgumentNullException(nameof(compilation));
}
if (analysisOptions == null)
{
throw new ArgumentNullException(nameof(analysisOptions));
}
VerifyAnalyzersArgumentForStaticApis(analyzers);
}
private static void VerifyAnalyzersArgumentForStaticApis(ImmutableArray<DiagnosticAnalyzer> analyzers, bool allowDefaultOrEmpty = false)
{
if (analyzers.IsDefaultOrEmpty)
{
if (allowDefaultOrEmpty)
{
return;
}
throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(analyzers));
}
if (analyzers.Any(a => a == null))
{
throw new ArgumentException(CodeAnalysisResources.ArgumentElementCannotBeNull, nameof(analyzers));
}
if (analyzers.Distinct().Length != analyzers.Length)
{
// Has duplicate analyzer instances.
throw new ArgumentException(CodeAnalysisResources.DuplicateAnalyzerInstances, nameof(analyzers));
}
}
private void VerifyAnalyzerArgument(DiagnosticAnalyzer analyzer)
{
VerifyAnalyzerArgumentForStaticApis(analyzer);
if (!_analyzers.Contains(analyzer))
{
throw new ArgumentException(CodeAnalysisResources.UnsupportedAnalyzerInstance, nameof(analyzer));
}
}
private static void VerifyAnalyzerArgumentForStaticApis(DiagnosticAnalyzer analyzer)
{
if (analyzer == null)
{
throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(analyzer));
}
}
private void VerifyExistingAnalyzersArgument(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
VerifyAnalyzersArgumentForStaticApis(analyzers);
if (analyzers.Any(a => !_analyzers.Contains(a)))
{
throw new ArgumentException(CodeAnalysisResources.UnsupportedAnalyzerInstance, nameof(_analyzers));
}
if (analyzers.Distinct().Length != analyzers.Length)
{
// Has duplicate analyzer instances.
throw new ArgumentException(CodeAnalysisResources.DuplicateAnalyzerInstances, nameof(analyzers));
}
}
private void VerifyModel(SemanticModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
if (!_compilation.ContainsSyntaxTree(model.SyntaxTree))
{
throw new ArgumentException(CodeAnalysisResources.InvalidTree, nameof(model));
}
}
private void VerifyTree(SyntaxTree tree)
{
if (tree == null)
{
throw new ArgumentNullException(nameof(tree));
}
if (!_compilation.ContainsSyntaxTree(tree))
{
throw new ArgumentException(CodeAnalysisResources.InvalidTree, nameof(tree));
}
}
private void VerifyAdditionalFile(AdditionalText file)
{
if (file == null)
{
throw new ArgumentNullException(nameof(file));
}
if (_analysisOptions.Options == null || !_analysisOptions.Options.AdditionalFiles.Contains(file))
{
throw new ArgumentException(CodeAnalysisResources.InvalidAdditionalFile, nameof(file));
}
}
#endregion
/// <summary>
/// Returns diagnostics produced by all <see cref="Analyzers"/>.
/// </summary>
public Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsAsync()
{
return GetAnalyzerDiagnosticsAsync(_cancellationToken);
}
/// <summary>
/// Returns diagnostics produced by all <see cref="Analyzers"/>.
/// </summary>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsAsync(CancellationToken cancellationToken)
{
return await GetAnalyzerDiagnosticsWithoutStateTrackingAsync(Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns diagnostics produced by given <paramref name="analyzers"/>.
/// </summary>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalyzerDiagnosticsWithoutStateTrackingAsync(analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Executes all <see cref="Analyzers"/> and returns the corresponding <see cref="AnalysisResult"/> with all diagnostics and telemetry info.
/// </summary>
public async Task<AnalysisResult> GetAnalysisResultAsync(CancellationToken cancellationToken)
{
return await GetAnalysisResultWithoutStateTrackingAsync(Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Executes the given <paramref name="analyzers"/> and returns the corresponding <see cref="AnalysisResult"/> with all diagnostics and telemetry info.
/// </summary>
/// <param name="analyzers">Analyzers whose analysis results are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<AnalysisResult> GetAnalysisResultAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalysisResultWithoutStateTrackingAsync(analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns all diagnostics produced by compilation and by all <see cref="Analyzers"/>.
/// </summary>
public Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsAsync()
{
return GetAllDiagnosticsAsync(_cancellationToken);
}
/// <summary>
/// Returns all diagnostics produced by compilation and by all <see cref="Analyzers"/>.
/// </summary>
public async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsAsync(CancellationToken cancellationToken)
{
var diagnostics = await GetAllDiagnosticsWithoutStateTrackingAsync(Analyzers, cancellationToken: cancellationToken).ConfigureAwait(false);
return diagnostics.AddRange(_exceptionDiagnostics);
}
/// <summary>
/// Returns diagnostics produced by compilation actions of all <see cref="Analyzers"/>.
/// </summary>
[Obsolete("This API was found to have performance issues and hence has been deprecated. Instead, invoke the API 'GetAnalysisResultAsync' and access the property 'CompilationDiagnostics' on the returned 'AnalysisResult' to fetch the compilation diagnostics.")]
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerCompilationDiagnosticsAsync(CancellationToken cancellationToken)
{
return await GetAnalyzerCompilationDiagnosticsCoreAsync(Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns diagnostics produced by compilation actions of given <paramref name="analyzers"/>.
/// </summary>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
[Obsolete("This API was found to have performance issues and hence has been deprecated. Instead, invoke the API 'GetAnalysisResultAsync' and access the property 'CompilationDiagnostics' on the returned 'AnalysisResult' to fetch the compilation diagnostics.")]
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerCompilationDiagnosticsAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalyzerCompilationDiagnosticsCoreAsync(analyzers, cancellationToken).ConfigureAwait(false);
}
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerCompilationDiagnosticsCoreAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
// Wait for all active tasks to complete.
await WaitForActiveAnalysisTasksAsync(waitForTreeTasks: true, waitForCompilationOrNonConcurrentTask: true, cancellationToken: cancellationToken).ConfigureAwait(false);
var diagnostics = ImmutableArray<Diagnostic>.Empty;
var hasAllAnalyzers = analyzers.Length == Analyzers.Length;
var analysisScope = new AnalysisScope(_compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
Func<ImmutableArray<CompilationEvent>> getPendingEvents = () =>
_analysisState.GetPendingEvents(analyzers, includeSourceEvents: true, includeNonSourceEvents: true, cancellationToken);
// Compute the analyzer diagnostics for the given analysis scope.
await ComputeAnalyzerDiagnosticsAsync(analysisScope, getPendingEvents, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false);
// Return computed non-local diagnostics for the given analysis scope.
return _analysisResultBuilder.GetDiagnostics(analysisScope, getLocalDiagnostics: false, getNonLocalDiagnostics: true);
}
private async Task<AnalysisResult> GetAnalysisResultWithoutStateTrackingAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
// PERF: Compute whole compilation diagnostics without doing any partial analysis state tracking.
await ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(cancellationToken).ConfigureAwait(false);
return _analysisResultBuilder.ToAnalysisResult(analyzers, cancellationToken);
}
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsWithoutStateTrackingAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
// PERF: Compute whole compilation diagnostics without doing any partial analysis state tracking.
await ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(cancellationToken).ConfigureAwait(false);
// Get analyzer diagnostics for the given analysis scope.
var hasAllAnalyzers = analyzers.Length == Analyzers.Length;
var analysisScope = new AnalysisScope(_compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
return _analysisResultBuilder.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: true);
}
private static AnalyzerDriver CreateDriverForComputingDiagnosticsWithoutStateTracking(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers)
{
// Create and attach a new driver instance to compilation, do not used a pooled instance from '_driverPool'.
// Additionally, use a new AnalyzerManager and don't use the analyzer manager instance
// stored onto the field '_analyzerManager'.
// Pooled _driverPool and shared _analyzerManager is used for partial/state-based analysis, and
// we want to compute diagnostics without any state tracking here.
return compilation.CreateAnalyzerDriver(analyzers, new AnalyzerManager(analyzers), severityFilter: SeverityFilter.None);
}
private async Task ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(CancellationToken cancellationToken)
{
// Exclude analyzers that have fully executed.
var analyzers = _analysisResultBuilder.GetPendingAnalyzers(this.Analyzers);
if (analyzers.IsEmpty)
{
return;
}
AsyncQueue<CompilationEvent> eventQueue = _eventQueuePool.Allocate();
AnalyzerDriver? driver = null;
try
{
// Clone the compilation with new event queue.
var compilation = _compilation.WithEventQueue(eventQueue);
var compilationData = new CompilationData(compilation);
// Create and attach the driver to compilation.
var categorizeDiagnostics = true;
driver = CreateDriverForComputingDiagnosticsWithoutStateTracking(compilation, analyzers);
driver.Initialize(compilation, _analysisOptions, compilationData, categorizeDiagnostics, cancellationToken);
var hasAllAnalyzers = analyzers.Length == Analyzers.Length;
var analysisScope = new AnalysisScope(compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics);
driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, cancellationToken);
// Force compilation diagnostics and wait for analyzer execution to complete.
var compDiags = compilation.GetDiagnostics(cancellationToken);
await driver.WhenCompletedTask.ConfigureAwait(false);
// Get analyzer action counts.
var analyzerActionCounts = new Dictionary<DiagnosticAnalyzer, AnalyzerActionCounts>(analyzers.Length);
foreach (var analyzer in analyzers)
{
var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, compilation.Options, cancellationToken).ConfigureAwait(false);
analyzerActionCounts.Add(analyzer, actionCounts);
}
Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts = analyzer => analyzerActionCounts[analyzer];
_analysisResultBuilder.ApplySuppressionsAndStoreAnalysisResult(analysisScope, driver, compilation, getAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: true);
}
finally
{
driver?.Dispose();
FreeEventQueue(eventQueue, _eventQueuePool);
}
}
private async Task<ImmutableArray<Diagnostic>> GetAllDiagnosticsWithoutStateTrackingAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
AsyncQueue<CompilationEvent> eventQueue = _eventQueuePool.Allocate();
AnalyzerDriver? driver = null;
try
{
// Clone the compilation with new event queue.
var compilation = _compilation.WithEventQueue(eventQueue);
var compilationData = new CompilationData(compilation);
// Create and attach the driver to compilation.
var categorizeDiagnostics = false;
driver = CreateDriverForComputingDiagnosticsWithoutStateTracking(compilation, analyzers);
driver.Initialize(compilation, _analysisOptions, compilationData, categorizeDiagnostics, cancellationToken);
var hasAllAnalyzers = analyzers.Length == Analyzers.Length;
var analysisScope = new AnalysisScope(compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics);
driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, cancellationToken);
// Force compilation diagnostics and wait for analyzer execution to complete.
var compDiags = compilation.GetDiagnostics(cancellationToken);
var analyzerDiags = await driver.GetDiagnosticsAsync(compilation).ConfigureAwait(false);
var reportedDiagnostics = compDiags.AddRange(analyzerDiags);
return driver.ApplyProgrammaticSuppressions(reportedDiagnostics, compilation);
}
finally
{
driver?.Dispose();
FreeEventQueue(eventQueue, _eventQueuePool);
}
}
/// <summary>
/// Returns syntax diagnostics produced by all <see cref="Analyzers"/> from analyzing the given <paramref name="tree"/>.
/// Depending on analyzers' behavior, returned diagnostics can have locations outside the tree,
/// and some diagnostics that would be reported for the tree by an analysis of the complete compilation
/// can be absent.
/// </summary>
/// <param name="tree">Syntax tree to analyze.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerSyntaxDiagnosticsAsync(SyntaxTree tree, CancellationToken cancellationToken)
{
VerifyTree(tree);
return await GetAnalyzerSyntaxDiagnosticsCoreAsync(tree, Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns syntax diagnostics produced by given <paramref name="analyzers"/> from analyzing the given <paramref name="tree"/>.
/// Depending on analyzers' behavior, returned diagnostics can have locations outside the tree,
/// and some diagnostics that would be reported for the tree by an analysis of the complete compilation
/// can be absent.
/// </summary>
/// <param name="tree">Syntax tree to analyze.</param>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerSyntaxDiagnosticsAsync(SyntaxTree tree, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyTree(tree);
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalyzerSyntaxDiagnosticsCoreAsync(tree, analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.SyntaxDiagnostics"/> produced by all <see cref="Analyzers"/> from analyzing the given <paramref name="tree"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="tree">Syntax tree to analyze.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task<AnalysisResult> GetAnalysisResultAsync(SyntaxTree tree, CancellationToken cancellationToken)
{
VerifyTree(tree);
return GetAnalysisResultCoreAsync(new SourceOrAdditionalFile(tree), Analyzers, cancellationToken);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.SyntaxDiagnostics"/> produced by given <paramref name="analyzers"/> from analyzing the given <paramref name="tree"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="tree">Syntax tree to analyze.</param>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task<AnalysisResult> GetAnalysisResultAsync(SyntaxTree tree, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyTree(tree);
VerifyExistingAnalyzersArgument(analyzers);
return GetAnalysisResultCoreAsync(new SourceOrAdditionalFile(tree), analyzers, cancellationToken);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.AdditionalFileDiagnostics"/> produced by all <see cref="Analyzers"/> from analyzing the given additional <paramref name="file"/>.
/// The given <paramref name="file"/> must be part of <see cref="AnalyzerOptions.AdditionalFiles"/> for the <see cref="AnalysisOptions"/> for this CompilationWithAnalyzers instance.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the file by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="file">Additional file to analyze.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<AnalysisResult> GetAnalysisResultAsync(AdditionalText file, CancellationToken cancellationToken)
{
VerifyAdditionalFile(file);
return await GetAnalysisResultCoreAsync(new SourceOrAdditionalFile(file), Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.AdditionalFileDiagnostics"/> produced by given <paramref name="analyzers"/> from analyzing the given additional <paramref name="file"/>.
/// The given <paramref name="file"/> must be part of <see cref="AnalyzerOptions.AdditionalFiles"/> for the <see cref="AnalysisOptions"/> for this CompilationWithAnalyzers instance.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the file by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="file">Additional file to analyze.</param>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<AnalysisResult> GetAnalysisResultAsync(AdditionalText file, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyAdditionalFile(file);
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalysisResultCoreAsync(new SourceOrAdditionalFile(file), analyzers, cancellationToken).ConfigureAwait(false);
}
private async Task<AnalysisResult> GetAnalysisResultCoreAsync(SourceOrAdditionalFile file, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
var analysisScope = new AnalysisScope(analyzers, file, filterSpan: null, isSyntacticSingleFileAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
await ComputeAnalyzerSyntaxDiagnosticsAsync(analysisScope, cancellationToken).ConfigureAwait(false);
return _analysisResultBuilder.ToAnalysisResult(analyzers, cancellationToken);
}
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSyntaxDiagnosticsCoreAsync(SyntaxTree tree, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
var analysisScope = new AnalysisScope(analyzers, new SourceOrAdditionalFile(tree), filterSpan: null, isSyntacticSingleFileAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
await ComputeAnalyzerSyntaxDiagnosticsAsync(analysisScope, cancellationToken).ConfigureAwait(false);
return _analysisResultBuilder.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false);
}
private async Task ComputeAnalyzerSyntaxDiagnosticsAsync(AnalysisScope analysisScope, CancellationToken cancellationToken)
{
try
{
var taskToken = Interlocked.Increment(ref _currentToken);
var pendingAnalyzers = _analysisResultBuilder.GetPendingAnalyzers(analysisScope.Analyzers);
if (pendingAnalyzers.Length > 0)
{
var pendingAnalysisScope = pendingAnalyzers.Length < analysisScope.Analyzers.Length ? analysisScope.WithAnalyzers(pendingAnalyzers, hasAllAnalyzers: false) : analysisScope;
// Compute the analyzer diagnostics for the pending analysis scope.
await ComputeAnalyzerDiagnosticsAsync(pendingAnalysisScope, getPendingEventsOpt: null, taskToken, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
{
throw ExceptionUtilities.Unreachable;
}
}
/// <summary>
/// Returns semantic diagnostics produced by all <see cref="Analyzers"/> from analyzing the given <paramref name="model"/>, optionally scoped to a <paramref name="filterSpan"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="model">Semantic model representing the syntax tree to analyze.</param>
/// <param name="filterSpan">An optional span within the tree to scope analysis.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsAsync(SemanticModel model, TextSpan? filterSpan, CancellationToken cancellationToken)
{
VerifyModel(model);
return await GetAnalyzerSemanticDiagnosticsCoreAsync(model, filterSpan, Analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns semantic diagnostics produced by the given <paramref name="analyzers"/> from analyzing the given <paramref name="model"/>, optionally scoped to a <paramref name="filterSpan"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="model">Semantic model representing the syntax tree to analyze.</param>
/// <param name="filterSpan">An optional span within the tree to scope analysis.</param>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyModel(model);
VerifyExistingAnalyzersArgument(analyzers);
return await GetAnalyzerSemanticDiagnosticsCoreAsync(model, filterSpan, analyzers, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.SemanticDiagnostics"/> produced by all <see cref="Analyzers"/> from analyzing the given <paramref name="model"/>, optionally scoped to a <paramref name="filterSpan"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="model">Semantic model representing the syntax tree to analyze.</param>
/// <param name="filterSpan">An optional span within the tree to scope analysis.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task<AnalysisResult> GetAnalysisResultAsync(SemanticModel model, TextSpan? filterSpan, CancellationToken cancellationToken)
{
VerifyModel(model);
return GetAnalysisResultCoreAsync(model, filterSpan, this.Analyzers, cancellationToken);
}
/// <summary>
/// Returns an <see cref="AnalysisResult"/> populated with <see cref="AnalysisResult.SemanticDiagnostics"/> produced by the given <paramref name="analyzers"/> from analyzing the given <paramref name="model"/>, optionally scoped to a <paramref name="filterSpan"/>.
/// Depending on analyzers' behavior, some diagnostics that would be reported for the tree by an analysis of the complete compilation can be absent.
/// </summary>
/// <param name="model">Semantic model representing the syntax tree to analyze.</param>
/// <param name="filterSpan">An optional span within the tree to scope analysis.</param>
/// <param name="analyzers">Analyzers whose diagnostics are required. All the given analyzers must be from the analyzers passed into the constructor of <see cref="CompilationWithAnalyzers"/>.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task<AnalysisResult> GetAnalysisResultAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
VerifyModel(model);
VerifyExistingAnalyzersArgument(analyzers);
return GetAnalysisResultCoreAsync(model, filterSpan, analyzers, cancellationToken);
}
private async Task<AnalysisResult> GetAnalysisResultCoreAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
var analysisScope = new AnalysisScope(analyzers, new SourceOrAdditionalFile(model.SyntaxTree), filterSpan, isSyntacticSingleFileAnalysis: false, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
await ComputeAnalyzerSemanticDiagnosticsAsync(model, analysisScope, cancellationToken).ConfigureAwait(false);
return _analysisResultBuilder.ToAnalysisResult(analyzers, cancellationToken);
}
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsCoreAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
var analysisScope = new AnalysisScope(analyzers, new SourceOrAdditionalFile(model.SyntaxTree), filterSpan, isSyntacticSingleFileAnalysis: false, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
await ComputeAnalyzerSemanticDiagnosticsAsync(model, analysisScope, cancellationToken).ConfigureAwait(false);
return _analysisResultBuilder.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false);
}
private async Task ComputeAnalyzerSemanticDiagnosticsAsync(SemanticModel model, AnalysisScope analysisScope, CancellationToken cancellationToken, bool forceCompletePartialTrees = true)
{
try
{
var taskToken = Interlocked.Increment(ref _currentToken);
var pendingAnalyzers = _analysisResultBuilder.GetPendingAnalyzers(analysisScope.Analyzers);
if (pendingAnalyzers.Length > 0)
{
var pendingAnalysisScope = pendingAnalyzers.Length < analysisScope.Analyzers.Length ? analysisScope.WithAnalyzers(pendingAnalyzers, hasAllAnalyzers: false) : analysisScope;
Func<ImmutableArray<CompilationEvent>> getPendingEvents = () => _analysisState.GetPendingEvents(analysisScope.Analyzers, model.SyntaxTree, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
// Compute the analyzer diagnostics for the given analysis scope.
(ImmutableArray<CompilationEvent> compilationEvents, bool hasSymbolStartActions) = await ComputeAnalyzerDiagnosticsAsync(pendingAnalysisScope, getPendingEvents, taskToken, cancellationToken).ConfigureAwait(false);
// If required, force compute diagnostics for partial symbol locations.
if (hasSymbolStartActions && forceCompletePartialTrees)
{
await processPartialSymbolLocationsAsync(compilationEvents, analysisScope).ConfigureAwait(false);
}
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
async Task processPartialSymbolLocationsAsync(ImmutableArray<CompilationEvent> compilationEvents, AnalysisScope analysisScope)
{
if (compilationEvents.IsDefaultOrEmpty)
{
return;
}
if (analysisScope.FilterSpanOpt.HasValue && !analysisScope.ContainsSpan(model.SyntaxTree.GetRoot(cancellationToken).Span))
{
// Analyzing part of the tree.
return;
}
HashSet<SyntaxTree>? partialTrees = null;
foreach (var compilationEvent in compilationEvents)
{
// Force complete partial declarations, except namespace declarations.
if (compilationEvent is SymbolDeclaredCompilationEvent symbolDeclaredEvent &&
symbolDeclaredEvent.Symbol.Kind != SymbolKind.Namespace &&
symbolDeclaredEvent.Symbol.Locations.Length > 1)
{
foreach (var location in symbolDeclaredEvent.Symbol.Locations)
{
if (location.SourceTree != null && location.SourceTree != model.SyntaxTree)
{
partialTrees = partialTrees ?? new HashSet<SyntaxTree>();
partialTrees.Add(location.SourceTree);
}
}
}
}
if (partialTrees != null)
{
if (AnalysisOptions.ConcurrentAnalysis)
{
await Task.WhenAll(partialTrees.Select(tree =>
Task.Run(() =>
{
var treeModel = _compilation.GetSemanticModel(tree);
analysisScope = new AnalysisScope(analysisScope.Analyzers, new SourceOrAdditionalFile(tree), filterSpan: null, isSyntacticSingleFileAnalysis: false, analysisScope.ConcurrentAnalysis, analysisScope.CategorizeDiagnostics);
return ComputeAnalyzerSemanticDiagnosticsAsync(treeModel, analysisScope, cancellationToken, forceCompletePartialTrees: false);
}, cancellationToken))).ConfigureAwait(false);
}
else
{
foreach (var tree in partialTrees)
{
cancellationToken.ThrowIfCancellationRequested();
var treeModel = _compilation.GetSemanticModel(tree);
analysisScope = new AnalysisScope(analysisScope.Analyzers, new SourceOrAdditionalFile(tree), filterSpan: null, isSyntacticSingleFileAnalysis: false, analysisScope.ConcurrentAnalysis, analysisScope.CategorizeDiagnostics);
await ComputeAnalyzerSemanticDiagnosticsAsync(treeModel, analysisScope, cancellationToken, forceCompletePartialTrees: false).ConfigureAwait(false);
}
}
}
}
}
private async Task<(ImmutableArray<CompilationEvent> events, bool hasSymbolStartActions)> ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Func<ImmutableArray<CompilationEvent>>? getPendingEventsOpt, int newTaskToken, CancellationToken cancellationToken)
{
try
{
AnalyzerDriver? driver = null;
Task? computeTask = null;
CancellationTokenSource cancellationSource;
try
{
// Get the analyzer driver to execute analysis.
driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false);
// Driver must have been initialized.
Debug.Assert(driver.IsInitialized);
Debug.Assert(!driver.WhenInitializedTask.IsCanceled);
cancellationToken.ThrowIfCancellationRequested();
GenerateCompilationEvents(analysisScope, cancellationToken);
await PopulateEventsCacheAsync(cancellationToken).ConfigureAwait(false);
// Track if this task was suspended by another tree diagnostics request for the same tree.
// If so, we wait for the high priority requests to complete before restarting analysis.
bool suspended;
var pendingEvents = ImmutableArray<CompilationEvent>.Empty;
do
{
suspended = false;
// Create a new cancellation source to allow higher priority requests to suspend our analysis.
using (cancellationSource = new CancellationTokenSource())
{
// Link the cancellation source with client supplied cancellation source, so the public API callee can also cancel analysis.
using var linkedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationSource.Token, cancellationToken);
try
{
// Fetch the cancellation token here to avoid capturing linkedCts in the getComputeTask lambda as the task may run after linkedCts has been disposed due to cancellation.
var linkedCancellationToken = linkedCancellationSource.Token;
// Core task to compute analyzer diagnostics.
Func<Tuple<Task, CancellationTokenSource>> getComputeTask = () => Tuple.Create(
Task.Run(async () =>
{
try
{
AsyncQueue<CompilationEvent> eventQueue = s_EmptyEventQueue;
try
{
// Get event queue with pending events to analyze.
if (getPendingEventsOpt != null)
{
pendingEvents = getPendingEventsOpt();
eventQueue = CreateEventsQueue(pendingEvents);
}
linkedCancellationToken.ThrowIfCancellationRequested();
// Execute analyzer driver on the given analysis scope with the given event queue.
await ComputeAnalyzerDiagnosticsCoreAsync(driver, eventQueue, analysisScope, cancellationToken: linkedCancellationToken).ConfigureAwait(false);
}
finally
{
FreeEventQueue(eventQueue, _eventQueuePool);
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}, linkedCancellationToken),
cancellationSource);
// Wait for higher priority tree document tasks to complete.
computeTask = await SetActiveAnalysisTaskAsync(getComputeTask, analysisScope.FilterFileOpt, newTaskToken, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await computeTask.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
cancellationToken.ThrowIfCancellationRequested();
if (!cancellationSource.IsCancellationRequested)
{
throw;
}
suspended = true;
}
finally
{
ClearExecutingTask(computeTask, analysisScope.FilterFileOpt);
computeTask = null;
}
}
} while (suspended);
return (pendingEvents, hasSymbolStartActions: driver?.HasSymbolStartedActions(analysisScope) ?? false);
}
finally
{
FreeDriver(driver);
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
{
throw ExceptionUtilities.Unreachable;
}
}
private void GenerateCompilationEvents(AnalysisScope analysisScope, CancellationToken cancellationToken)
{
// Invoke GetDiagnostics to populate CompilationEvent queue for the given analysis scope.
// Discard the returned diagnostics.
if (analysisScope.FilterFileOpt == null)
{
_ = _compilation.GetDiagnostics(cancellationToken);
}
else if (!analysisScope.IsSyntacticSingleFileAnalysis)
{
// Get the mapped model and invoke GetDiagnostics for the given filter span, if any.
// Limiting the GetDiagnostics scope to the filter span ensures we only generate compilation events
// for the required symbols whose declaration intersects with this span, instead of all symbols in the tree.
var mappedModel = _compilation.GetSemanticModel(analysisScope.FilterFileOpt!.Value.SourceTree!);
_ = mappedModel.GetDiagnostics(analysisScope.FilterSpanOpt, cancellationToken);
}
}
private async Task PopulateEventsCacheAsync(CancellationToken cancellationToken)
{
if (_compilation.EventQueue?.Count > 0)
{
AnalyzerDriver? driver = null;
try
{
driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
Func<AsyncQueue<CompilationEvent>, ImmutableArray<AdditionalText>, ImmutableArray<CompilationEvent>> getCompilationEvents =
(eventQueue, additionalFiles) => dequeueGeneratedCompilationEvents(eventQueue, additionalFiles);
var additionalFiles = _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray<AdditionalText>.Empty;
await _analysisState.OnCompilationEventsGeneratedAsync(getCompilationEvents, _compilation.EventQueue, additionalFiles, driver, cancellationToken).ConfigureAwait(false);
}
finally
{
FreeDriver(driver);
}
}
static ImmutableArray<CompilationEvent> dequeueGeneratedCompilationEvents(AsyncQueue<CompilationEvent> eventQueue, ImmutableArray<AdditionalText> additionalFiles)
{
var builder = ImmutableArray.CreateBuilder<CompilationEvent>();
while (eventQueue.TryDequeue(out CompilationEvent compilationEvent))
{
if (compilationEvent is CompilationStartedEvent compilationStartedEvent &&
!additionalFiles.IsEmpty)
{
compilationEvent = compilationStartedEvent.WithAdditionalFiles(additionalFiles);
}
builder.Add(compilationEvent);
}
return builder.ToImmutable();
}
}
private async Task<AnalyzerDriver> GetAnalyzerDriverAsync(CancellationToken cancellationToken)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
// Get instance of analyzer driver from the driver pool.
AnalyzerDriver driver = _driverPool.Allocate();
bool success = false;
try
{
// Start the initialization task, if required.
if (!driver.IsInitialized)
{
driver.Initialize(_compilation, _analysisOptions, _compilationData, categorizeDiagnostics: true, cancellationToken: cancellationToken);
}
// Wait for driver initialization to complete: this executes the Initialize and CompilationStartActions to compute all registered actions per-analyzer.
await driver.WhenInitializedTask.ConfigureAwait(false);
success = true;
return driver;
}
finally
{
if (!success)
{
FreeDriver(driver);
}
}
}
catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
{
throw ExceptionUtilities.Unreachable;
}
}
private void FreeDriver(AnalyzerDriver? driver)
{
if (driver != null)
{
// Throw away the driver instance if the initialization didn't succeed.
if (!driver.IsInitialized || driver.WhenInitializedTask.IsCanceled)
{
_driverPool.ForgetTrackedObject(driver);
}
else
{
_driverPool.Free(driver);
}
}
}
/// <summary>
/// Core method for executing analyzers.
/// </summary>
private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken)
{
try
{
Debug.Assert(driver.IsInitialized);
Debug.Assert(!driver.WhenInitializedTask.IsCanceled);
if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope))
{