private async Task EnsureAnalyzerActionCountsInitializedAsync( AnalyzerDriver driver, CancellationToken cancellationToken ) { if (_lazyAnalyzerActionCountsMap == null) { var builder = ImmutableDictionary.CreateBuilder < DiagnosticAnalyzer, AnalyzerActionCounts >(); foreach (var(analyzer, _) in _analyzerStateMap) { var actionCounts = await driver .GetAnalyzerActionCountsAsync( analyzer, _compilationOptions, cancellationToken ) .ConfigureAwait(false); builder.Add(analyzer, actionCounts); } Interlocked.CompareExchange( ref _lazyAnalyzerActionCountsMap, builder.ToImmutable(), null ); } }
public void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver) { foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer); lock (_gate) { if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics); } if (_analyzerExecutionTimeOpt != null) { _analyzerExecutionTimeOpt[analyzer] += driver.ResetAnalyzerExecutionTime(analyzer); } } } }
private async Task <AnalyzerDriver> GetAnalyzerDriverAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Get instance of analyzer driver from the driver pool. AnalyzerDriver driver = _driverPool.Allocate(); try { // Start the initialization task, if required. if (driver.WhenInitializedTask == null) { driver.Initialize(_compilation, _analysisOptions, categorizeDiagnostics: true, cancellationToken: cancellationToken); } // Wait for driver initilization to complete: this executes the Initialize and CompilationStartActions to compute all registered actions per-analyzer. await driver.WhenInitializedTask.ConfigureAwait(false); } finally { if (driver.WhenInitializedTask.IsCanceled) { // If the initialization task was cancelled, we retry again with our own cancellation token. // This can happen if the task that started the initialization was cancelled by the callee, and the new request picked up this driver instance. _driverPool.ForgetTrackedObject(driver); driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false); } } return(driver); }
/// <summary> /// Invoke this method at completion of event processing for the given analysis scope. /// It updates the analysis state of this event for each analyzer and if the event has been fully processed for all analyzers, then removes it from our event cache. /// </summary> public void OnCompilationEventProcessed(CompilationEvent compilationEvent, AnalysisScope analysisScope) { // Analyze if the symbol and all its declaring syntax references are analyzed. var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolDeclaredEvent != null) { OnSymbolDeclaredEventProcessed(symbolDeclaredEvent, analysisScope.Analyzers); } // Check if event is fully analyzed for all analyzers. foreach (var analyzerState in _analyzerStates) { if (!analyzerState.IsEventAnalyzed(compilationEvent)) { return; } } // Remove the event from event map. lock (_gate) { UpdateEventsMap_NoLock(compilationEvent, add: false); } if (symbolDeclaredEvent != null) { AnalyzerDriver.RemoveCachedDeclaringReferences(symbolDeclaredEvent.Symbol, symbolDeclaredEvent.Compilation); } }
/// <summary> /// Invoke this method at completion of event processing for the given analysis scope. /// It updates the analysis state of this event for each analyzer and if the event has been fully processed for all analyzers, then removes it from our event cache. /// </summary> public async Task OnCompilationEventProcessedAsync(CompilationEvent compilationEvent, AnalysisScope analysisScope, CancellationToken cancellationToken) { // Analyze if the symbol and all its declaring syntax references are analyzed. var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolDeclaredEvent != null) { await OnSymbolDeclaredEventProcessedAsync(symbolDeclaredEvent, analysisScope.Analyzers, cancellationToken).ConfigureAwait(false); } // Check if event is fully analyzed for all analyzers. foreach (var analyzerState in _analyzerStates) { var eventAnalyzed = await analyzerState.IsEventAnalyzedAsync(compilationEvent, cancellationToken).ConfigureAwait(false); if (!eventAnalyzed) { return; } } // Remove the event from event map. using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { UpdateEventsMap_NoLock(compilationEvent, add: false); } if (symbolDeclaredEvent != null) { AnalyzerDriver.RemoveCachedDeclaringReferences(symbolDeclaredEvent.Symbol, symbolDeclaredEvent.Compilation); } }
// internal for testing purposes internal static AnalyzerDriver Create( Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action <Diagnostic> addExceptionDiagnostic, out Compilation newCompilation, Func <Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken) { options = options ?? AnalyzerOptions.Empty; AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager, cancellationToken); newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue); continueOnAnalyzerException = continueOnAnalyzerException ?? ((exception, analyzer) => true); var addDiagnostic = GetDiagnosticSinkWithSuppression(analyzerDriver.DiagnosticQueue.Enqueue, newCompilation); addExceptionDiagnostic = addExceptionDiagnostic != null? GetDiagnosticSinkWithSuppression(addExceptionDiagnostic, newCompilation) : addDiagnostic; var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, addExceptionDiagnostic, continueOnAnalyzerException, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); return(analyzerDriver); }
private void GenerateSimulatedCompilationSourceEvents( SyntaxTree tree, Compilation compilation, Func <SyntaxTree, Compilation, CancellationToken, SemanticModel> getCachedSemanticModel, AnalyzerDriver driver, CancellationToken cancellationToken) { lock (_gate) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { return; } } var globalNs = compilation.Assembly.GlobalNamespace; var symbols = GetDeclaredSymbolsInTree(tree, compilation, getCachedSemanticModel, cancellationToken); var compilationEvents = CreateCompilationEventsForTree(symbols.Concat(globalNs), tree, compilation); lock (_gate) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { return; } OnCompilationEventsGenerated_NoLock(compilationEvents, tree, driver, cancellationToken); var added = _treesWithGeneratedSourceEvents.Add(tree); Debug.Assert(added); } }
public async Task OnCompilationEventsGeneratedAsync( Func < AsyncQueue <CompilationEvent>, ImmutableArray <AdditionalText>, ImmutableArray <CompilationEvent> > getCompilationEvents, AsyncQueue <CompilationEvent> eventQueue, ImmutableArray <AdditionalText> additionalFiles, AnalyzerDriver driver, CancellationToken cancellationToken ) { try { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken) .ConfigureAwait(false); using (_gate.DisposableWait(cancellationToken)) { // Defer the call to 'getCompilationEvents' until we know cancellation is no longer possible OnCompilationEventsGenerated_NoLock( getCompilationEvents(eventQueue, additionalFiles) ); } } catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { throw ExceptionUtilities.Unreachable; } }
internal async Task <AnalyzerActionCounts> GetOrComputeAnalyzerActionCountsAsync( DiagnosticAnalyzer analyzer, AnalyzerDriver driver, CancellationToken cancellationToken ) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken) .ConfigureAwait(false); return(AnalyzerActionCountsMap[analyzer]); }
/// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// <param name="analyzer">Analyzer to be checked for suppression.</param> /// <param name="options">Compilation options.</param> /// <param name="onAnalyzerException"> /// Optional delegate which is invoked when an analyzer throws an exception. /// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc. /// </param> /// </summary> public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Action <Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null) { VerifyAnalyzerArgumentForStaticApis(analyzer); if (options == null) { throw new ArgumentNullException(nameof(options)); } var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance); return(AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor)); }
/// <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) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } VerifyAnalyzersArgument(analyzers); _cancellationToken = cancellationToken; _exceptionDiagnostics = new ConcurrentSet <Diagnostic>(); _driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, false, out _compilation, _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) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } VerifyAnalyzersArgument(analyzers); _cancellationToken = cancellationToken; _exceptionDiagnostics = new ConcurrentSet<Diagnostic>(); _driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, false, out _compilation, _cancellationToken); }
private void AddPendingSourceEvent_NoLock(SyntaxTree tree, CompilationEvent compilationEvent) { HashSet <CompilationEvent> currentEvents; if (!_pendingSourceEvents.TryGetValue(tree, out currentEvents)) { currentEvents = _compilationEventsPool.Allocate(); _pendingSourceEvents[tree] = currentEvents; AnalyzerDriver.RemoveCachedSemanticModel(tree, compilationEvent.Compilation); } currentEvents.Add(compilationEvent); }
private void FreeDriver(AnalyzerDriver driver) { if (driver != null) { if (driver.WhenInitializedTask.IsCanceled) { _driverPool.ForgetTrackedObject(driver); } else { _driverPool.Free(driver); } } }
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope) { Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis."); foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation); lock (_gate) { if (_completedAnalyzers.Contains(analyzer)) { // Already stored full analysis result for this analyzer. continue; } if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope); } if (_analyzerExecutionTimeOpt != null) { var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer); _analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ? timeSpan : _analyzerExecutionTimeOpt[analyzer] + timeSpan; } if (!_analyzerActionCounts.ContainsKey(analyzer)) { _analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer)); } if (fullAnalysisResultForAnalyzersInScope) { _completedAnalyzers.Add(analyzer); } } } }
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func <DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope) { Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis."); foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation); lock (_gate) { if (_completedAnalyzers.Contains(analyzer)) { // Already stored full analysis result for this analyzer. continue; } if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope); } if (_analyzerExecutionTimeOpt != null) { var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer); _analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ? timeSpan : _analyzerExecutionTimeOpt[analyzer] + timeSpan; } if (!_analyzerActionCounts.ContainsKey(analyzer)) { _analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer)); } if (fullAnalysisResultForAnalyzersInScope) { _completedAnalyzers.Add(analyzer); } } } }
/// <summary> /// Gets the count of registered actions for the analyzer. /// </summary> internal async Task <ActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { VerifyAnalyzerArgument(analyzer); AnalyzerDriver driver = null; try { driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return(await _analysisState.GetAnalyzerActionCountsAsync(analyzer, driver, cancellationToken).ConfigureAwait(false)); } finally { FreeDriver(driver); } }
/// <summary> /// Core method for executing analyzers. /// </summary> private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, AsyncQueue <CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken) { Debug.Assert(!driver.WhenInitializedTask.IsCanceled); if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope)) { try { // Perform analysis to compute new diagnostics. Debug.Assert(!eventQueue.IsCompleted); await driver.AttachQueueAndProcessAllEventsAsync(eventQueue, analysisScope, _analysisState, cancellationToken : cancellationToken).ConfigureAwait(false); } finally { // Update the diagnostic results based on the diagnostics reported on the driver. _analysisResult.StoreAnalysisResult(analysisScope, driver); } } }
/// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// <param name="analyzer">Analyzer to be checked for suppression.</param> /// <param name="options">Compilation options.</param> /// <param name="onAnalyzerException"> /// Optional delegate which is invoked when an analyzer throws an exception. /// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc. /// </param> /// </summary> public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Action <Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null) { if (analyzer == null) { throw new ArgumentNullException(nameof(analyzer)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } Action <Exception, DiagnosticAnalyzer, Diagnostic> voidHandler = (ex, a, diag) => { }; onAnalyzerException = onAnalyzerException ?? voidHandler; var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, CancellationToken.None); return(AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor)); }
public static Diagnostic ApplySourceSuppressions(Diagnostic diagnostic, Compilation compilation, ISymbol symbolOpt = null) { if (diagnostic.IsSuppressed) { // Diagnostic already has a source suppression. return(diagnostic); } var suppressMessageState = AnalyzerDriver.GetOrCreateCachedCompilationData(compilation).SuppressMessageAttributeState; SuppressMessageInfo info; if (suppressMessageState.IsDiagnosticSuppressed(diagnostic, out info)) { // Attach the suppression info to the diagnostic. diagnostic = diagnostic.WithIsSuppressed(true); } return(diagnostic); }
private async Task PopulateEventsCacheAsync(CancellationToken cancellationToken) { if (_compilation.EventQueue.Count > 0) { AnalyzerDriver driver = null; try { driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); var compilationEvents = DequeueGeneratedCompilationEvents(); await _analysisState.OnCompilationEventsGeneratedAsync(compilationEvents, driver, cancellationToken).ConfigureAwait(false); } finally { FreeDriver(driver); } } }
/// <summary> /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it. /// The returned diagnostics are then ordered by location in the source document. /// </summary> /// <param name="analyzer">The analyzer to run on the documents</param> /// <param name="documents">The Documents that the analyzer will be run on</param> /// <param name="spans">Optional TextSpan indicating where a Diagnostic will be found</param> /// <returns>An IEnumerable of Diagnostics that surfaced in teh source code, sorted by Location</returns> protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(IDiagnosticAnalyzer analyzer, Document[] documents) { var projects = new HashSet<Project>(); foreach (var document in documents) { projects.Add(document.Project); } var diagnostics = new List<Diagnostic>(); foreach (var project in projects) { var compilation = project.GetCompilationAsync().Result; var driver = new AnalyzerDriver<SyntaxKind>((new[] { analyzer }).ToImmutableArray(), n => n.CSharpKind(), null, CancellationToken.None); compilation = compilation.WithEventQueue(driver.CompilationEventQueue); var discarded = compilation.GetDiagnostics(); var diags = driver.GetDiagnosticsAsync().Result; foreach (var diag in diags) { if (diag.Location == Location.None || diag.Location.IsInMetadata) { diagnostics.Add(diag); } else { for (int i = 0; i < documents.Length; i++) { var document = documents[i]; var tree = document.GetSyntaxTreeAsync().Result; if (tree == diag.Location.SourceTree) { diagnostics.Add(diag); } } } } } var results = SortDiagnostics(diagnostics); diagnostics.Clear(); return results; }
/// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// <paramref name="continueOnAnalyzerException"/> says whether the caller would like the exception thrown by the analyzers to be handled or not. If true - Handles ; False - Not handled. /// </summary> public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Func <Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException) { if (analyzer == null) { throw new ArgumentNullException(nameof(analyzer)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (continueOnAnalyzerException == null) { throw new ArgumentNullException(nameof(continueOnAnalyzerException)); } Action <Diagnostic> dummy = _ => { }; return(AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, dummy, continueOnAnalyzerException, CancellationToken.None)); }
/// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// <paramref name="continueOnAnalyzerException"/> says whether the caller would like the exception thrown by the analyzers to be handled or not. If true - Handles ; False - Not handled. /// </summary> public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Func <Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException) { if (analyzer == null) { throw new ArgumentNullException(nameof(analyzer)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (continueOnAnalyzerException == null) { throw new ArgumentNullException(nameof(continueOnAnalyzerException)); } // TODO: Public API change to surface exception diagnostics? Action <Diagnostic> addExceptionDiagnostic = diagnostic => { }; var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(addExceptionDiagnostic, continueOnAnalyzerException, CancellationToken.None); return(AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor)); }
// internal for testing purposes internal static AnalyzerDriver Create( Compilation compilation, ImmutableArray <DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action <Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, out Compilation newCompilation, CancellationToken cancellationToken) { options = options ?? AnalyzerOptions.Empty; AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager, cancellationToken); newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue); var addDiagnostic = GetDiagnosticSinkWithSuppression(analyzerDriver.DiagnosticQueue.Enqueue, newCompilation); Action <Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException; if (onAnalyzerException != null) { // Wrap onAnalyzerException to pass in filtered diagnostic. var comp = newCompilation; newOnAnalyzerException = (ex, analyzer, diagnostic) => onAnalyzerException(ex, analyzer, GetFilteredDiagnostic(diagnostic, comp)); } else { // Add exception diagnostic to regular diagnostic bag. newOnAnalyzerException = (ex, analyzer, diagnostic) => addDiagnostic(diagnostic); } var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); return(analyzerDriver); }
public async Task GenerateSimulatedCompilationEventsAsync( AnalysisScope analysisScope, Compilation compilation, Func <SyntaxTree, Compilation, CancellationToken, SemanticModel> getCachedSemanticModel, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); // Compilation started event. GenerateSimulatedCompilationNonSourceEvent(compilation, driver, started: true, cancellationToken: cancellationToken); // Symbol declared and compilation unit completed events. foreach (var tree in analysisScope.SyntaxTrees) { GenerateSimulatedCompilationSourceEvents(tree, compilation, getCachedSemanticModel, driver, cancellationToken); } // Compilation ended event. if (analysisScope.FilterTreeOpt == null) { GenerateSimulatedCompilationNonSourceEvent(compilation, driver, started: false, cancellationToken: cancellationToken); } }
private void FreeDriver(AnalyzerDriver driver) { if (driver != null) { // Throw away the driver instance if the initialization didn't succeed. if (driver.WhenInitializedTask == null || driver.WhenInitializedTask.IsCanceled) { _driverPool.ForgetTrackedObject(driver); } else { _driverPool.Free(driver); } } }
private void UpdateEventsMap_NoLock(CompilationEvent compilationEvent, bool add) { var symbolEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { // Add/remove symbol events. // Any diagnostics request for a tree should trigger symbol and syntax node analysis for symbols with at least one declaring reference in the tree. foreach (var location in symbolEvent.Symbol.Locations) { if (location.SourceTree != null) { if (add) { AddPendingSourceEvent_NoLock(location.SourceTree, compilationEvent); } else { RemovePendingSourceEvent_NoLock(location.SourceTree, compilationEvent); } } } } else { // Add/remove compilation unit completed events. var compilationUnitCompletedEvent = compilationEvent as CompilationUnitCompletedEvent; if (compilationUnitCompletedEvent != null) { var tree = compilationUnitCompletedEvent.SemanticModel.SyntaxTree; if (add) { AddPendingSourceEvent_NoLock(tree, compilationEvent); } else { RemovePendingSourceEvent_NoLock(tree, compilationEvent); } } else if (compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent) { // Add/remove compilation events. if (add) { _pendingNonSourceEvents.Add(compilationEvent); } else { _pendingNonSourceEvents.Remove(compilationEvent); _compilationEndAnalyzed |= compilationEvent is CompilationCompletedEvent; } } else { throw new InvalidOperationException("Unexpected compilation event of type " + compilationEvent.GetType().Name); } } if (_compilationEndAnalyzed && _pendingSourceEvents.Count == 0) { // Clear the per-compilation data cache if we finished analyzing this compilation. AnalyzerDriver.RemoveCachedCompilationData(compilationEvent.Compilation); } }
public async Task OnCompilationEventsGeneratedAsync(ImmutableArray <CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); lock (_gate) { OnCompilationEventsGenerated_NoLock(compilationEvents, filterTreeOpt: null, driver: driver, cancellationToken: cancellationToken); } }
private async Task OnCompilationEventsGenerated_NoLockAsync(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { Debug.Assert(_lazyAnalyzerActionCountsMap != null); // Add the events to our global pending events map. AddToEventsMap_NoLock(compilationEvents); // Mark the events for analysis for each analyzer. Debug.Assert(_pooledEventsWithAnyActionsSet.Count == 0); foreach (var kvp in _analyzerStateMap) { var analyzer = kvp.Key; var analyzerState = _analyzerStates[kvp.Value]; var actionCounts = _lazyAnalyzerActionCountsMap[analyzer]; foreach (var compilationEvent in compilationEvents) { if (HasActionsForEvent(compilationEvent, actionCounts)) { _pooledEventsWithAnyActionsSet.Add(compilationEvent); await analyzerState.OnCompilationEventGeneratedAsync(compilationEvent, actionCounts, cancellationToken).ConfigureAwait(false); } } } foreach (var compilationEvent in compilationEvents) { if (!_pooledEventsWithAnyActionsSet.Remove(compilationEvent)) { // Event has no relevant actions to execute, so mark it as complete. UpdateEventsMap_NoLock(compilationEvent, add: false); } } }
public async Task OnCompilationEventsGeneratedAsync(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { await OnCompilationEventsGenerated_NoLockAsync(compilationEvents, driver, cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Core method for executing analyzers. /// </summary> private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken) { try { Debug.Assert(!driver.WhenInitializedTask.IsCanceled); if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope)) { try { // Perform analysis to compute new diagnostics. Debug.Assert(!eventQueue.IsCompleted); await driver.AttachQueueAndProcessAllEventsAsync(eventQueue, analysisScope, _analysisState, cancellationToken: cancellationToken).ConfigureAwait(false); } finally { // Update the diagnostic results based on the diagnostics reported on the driver. _analysisResult.StoreAnalysisResult(analysisScope, driver); } } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
internal async Task<ActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); return _lazyAnalyzerActionCountsMap[analyzer]; }
private async Task EnsureAnalyzerActionCountsInitializedAsync(AnalyzerDriver driver, CancellationToken cancellationToken) { if (_lazyAnalyzerActionCountsMap == null) { var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ActionCounts>(); foreach (var analyzer in _analyzerStateMap.Keys) { var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, cancellationToken).ConfigureAwait(false); builder.Add(analyzer, actionCounts); } Interlocked.CompareExchange(ref _lazyAnalyzerActionCountsMap, builder.ToImmutable(), null); } }
private void OnCompilationEventsGenerated_NoLock(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { Debug.Assert(_lazyAnalyzerActionCountsMap != null); // Add the events to our global pending events map. AddToEventsMap_NoLock(compilationEvents); // Mark the events for analysis for each analyzer. foreach (var kvp in _analyzerStateMap) { var analyzer = kvp.Key; var analyzerState = kvp.Value; var actionCounts = _lazyAnalyzerActionCountsMap[analyzer]; foreach (var compilationEvent in compilationEvents) { if (HasActionsForEvent(compilationEvent, actionCounts)) { analyzerState.OnCompilationEventGenerated_NoLock(compilationEvent, actionCounts); } } } }
private void OnCompilationEventsGenerated_NoLock(ImmutableArray<CompilationEvent> compilationEvents, SyntaxTree filterTreeOpt, AnalyzerDriver driver, CancellationToken cancellationToken) { Debug.Assert(_lazyAnalyzerActionCountsMap != null); // Add the events to our global pending events map. AddToEventsMap_NoLock(compilationEvents, filterTreeOpt); // Mark the events for analysis for each analyzer. ArrayBuilder<ISymbol> newPartialSymbols = null; Debug.Assert(_pooledEventsWithAnyActionsSet.Count == 0); foreach (var kvp in _analyzerStateMap) { var analyzer = kvp.Key; var analyzerState = _analyzerStates[kvp.Value]; var actionCounts = _lazyAnalyzerActionCountsMap[analyzer]; foreach (var compilationEvent in compilationEvents) { if (HasActionsForEvent(compilationEvent, actionCounts)) { _pooledEventsWithAnyActionsSet.Add(compilationEvent); var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolDeclaredEvent?.DeclaringSyntaxReferences.Length > 1) { if (_partialSymbolsWithGeneratedSourceEvents.Contains(symbolDeclaredEvent.Symbol)) { // already processed. continue; } else { newPartialSymbols = newPartialSymbols ?? ArrayBuilder<ISymbol>.GetInstance(); newPartialSymbols.Add(symbolDeclaredEvent.Symbol); } } analyzerState.OnCompilationEventGenerated(compilationEvent, actionCounts); } } } foreach (var compilationEvent in compilationEvents) { if (!_pooledEventsWithAnyActionsSet.Remove(compilationEvent)) { // Event has no relevant actions to execute, so mark it as complete. UpdateEventsMap_NoLock(compilationEvent, add: false); } } if (newPartialSymbols != null) { _partialSymbolsWithGeneratedSourceEvents.AddAll(newPartialSymbols); newPartialSymbols.Free(); } }
internal static Compilation AttachAnalyzerDriverToCompilation(Compilation compilation, ImmutableArray<IDiagnosticAnalyzer> analyzers, out AnalyzerDriver analyzerDriver3, AnalyzerOptions options, CancellationToken cancellationToken) { analyzerDriver3 = compilation.AnalyzerForLanguage(analyzers, options, cancellationToken); return compilation.WithEventQueue(analyzerDriver3.CompilationEventQueue); }
/// <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) { _cancellationToken = cancellationToken; _driver = AnalyzerDriver.Create(compilation, analyzers, options, out _compilation, _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) { _cancellationToken = cancellationToken; _exceptionDiagnostics = new ConcurrentSet<Diagnostic>(); _driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, out _compilation, _cancellationToken); }
public async Task OnCompilationEventsGeneratedAsync(ImmutableArray<CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); lock (_gate) { OnCompilationEventsGenerated_NoLock(compilationEvents, driver, cancellationToken); } }
public async Task OnCompilationEventsGeneratedAsync(ImmutableArray <CompilationEvent> compilationEvents, AnalyzerDriver driver, CancellationToken cancellationToken) { try { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); using (_gate.DisposableWait(cancellationToken)) { OnCompilationEventsGenerated_NoLock(compilationEvents); } } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task GenerateCompilationEventsAndPopulateEventsCacheAsync(AnalysisScope analysisScope, AnalyzerDriver driver, CancellationToken cancellationToken) { #if SIMULATED_EVENT_QUEUE await _analysisState.GenerateSimulatedCompilationEventsAsync(analysisScope, _compilation, _compilationData.GetOrCreateCachedSemanticModel, driver, cancellationToken).ConfigureAwait(false); #else GenerateCompilationEvents(analysisScope, cancellationToken); await PopulateEventsCacheAsync(cancellationToken).ConfigureAwait(false); #endif }
private static void ReportAnalyzerExecutionTime(TextWriter consoleOutput, AnalyzerDriver analyzerDriver, CultureInfo culture, bool isConcurrentBuild) { Debug.Assert(analyzerDriver.AnalyzerExecutionTimes != null); if (analyzerDriver.AnalyzerExecutionTimes.IsEmpty) { return; } var totalAnalyzerExecutionTime = analyzerDriver.AnalyzerExecutionTimes.Sum(kvp => kvp.Value.TotalSeconds); Func<double, string> getFormattedTime = d => d.ToString("##0.000", culture); consoleOutput.WriteLine(); consoleOutput.WriteLine(string.Format(CodeAnalysisResources.AnalyzerTotalExecutionTime, getFormattedTime(totalAnalyzerExecutionTime))); if (isConcurrentBuild) { consoleOutput.WriteLine(CodeAnalysisResources.MultithreadedAnalyzerExecutionNote); } var analyzersByAssembly = analyzerDriver.AnalyzerExecutionTimes .GroupBy(kvp => kvp.Key.GetType().GetTypeInfo().Assembly) .OrderByDescending(kvp => kvp.Sum(entry => entry.Value.Ticks)); consoleOutput.WriteLine(); getFormattedTime = d => d < 0.001 ? string.Format(culture, "{0,8:<0.000}", 0.001) : string.Format(culture, "{0,8:##0.000}", d); Func<int, string> getFormattedPercentage = i => string.Format("{0,5}", i < 1 ? "<1" : i.ToString()); Func<string, string> getFormattedAnalyzerName = s => " " + s; // Table header var analyzerTimeColumn = string.Format("{0,8}", CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader); var analyzerPercentageColumn = string.Format("{0,5}", "%"); var analyzerNameColumn = getFormattedAnalyzerName(CodeAnalysisResources.AnalyzerNameColumnHeader); consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); // Table rows grouped by assembly. foreach (var analyzerGroup in analyzersByAssembly) { var executionTime = analyzerGroup.Sum(kvp => kvp.Value.TotalSeconds); var percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); analyzerTimeColumn = getFormattedTime(executionTime); analyzerPercentageColumn = getFormattedPercentage(percentage); analyzerNameColumn = getFormattedAnalyzerName(analyzerGroup.Key.FullName); consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); // Rows for each diagnostic analyzer in the assembly. foreach (var kvp in analyzerGroup.OrderByDescending(kvp => kvp.Value)) { executionTime = kvp.Value.TotalSeconds; percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); analyzerTimeColumn = getFormattedTime(executionTime); analyzerPercentageColumn = getFormattedPercentage(percentage); analyzerNameColumn = getFormattedAnalyzerName(" " + kvp.Key.ToString()); consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); } consoleOutput.WriteLine(); } }
private void GenerateSimulatedCompilatioNonSourceEvent(Compilation compilation, AnalyzerDriver driver, bool started, CancellationToken cancellationToken) { lock (_gate) { var eventAlreadyGenerated = started ? _compilationStartGenerated : _compilationEndGenerated; if (eventAlreadyGenerated) { return; } var compilationEvent = started ? (CompilationEvent)new CompilationStartedEvent(compilation) : new CompilationCompletedEvent(compilation); var events = ImmutableArray.Create(compilationEvent); OnCompilationEventsGenerated_NoLock(events, filterTreeOpt: null, driver: driver, cancellationToken: cancellationToken); if (started) { _compilationStartGenerated = true; } else { _compilationEndGenerated = true; } } }
private void GenerateSimulatedCompilationSourceEvents( SyntaxTree tree, Compilation compilation, Func<SyntaxTree, Compilation, CancellationToken, SemanticModel> getCachedSemanticModel, AnalyzerDriver driver, CancellationToken cancellationToken) { lock (_gate) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { return; } } var globalNs = compilation.Assembly.GlobalNamespace; var symbols = GetDeclaredSymbolsInTree(tree, compilation, getCachedSemanticModel, cancellationToken); var compilationEvents = CreateCompilationEventsForTree(symbols.Concat(globalNs), tree, compilation); lock (_gate) { if (_treesWithGeneratedSourceEvents.Contains(tree)) { return; } OnCompilationEventsGenerated_NoLock(compilationEvents, tree, driver, cancellationToken); var added = _treesWithGeneratedSourceEvents.Add(tree); Debug.Assert(added); } }
public async Task GenerateSimulatedCompilationEventsAsync( AnalysisScope analysisScope, Compilation compilation, Func<SyntaxTree, Compilation, CancellationToken, SemanticModel> getCachedSemanticModel, AnalyzerDriver driver, CancellationToken cancellationToken) { await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false); // Compilation started event. GenerateSimulatedCompilatioNonSourceEvent(compilation, driver, started: true, cancellationToken: cancellationToken); // Symbol declared and compilation unit completed events. foreach (var tree in analysisScope.SyntaxTrees) { GenerateSimulatedCompilationSourceEvents(tree, compilation, getCachedSemanticModel, driver, cancellationToken); } // Compilation ended event. if (analysisScope.FilterTreeOpt == null) { GenerateSimulatedCompilatioNonSourceEvent(compilation, driver, started: false, cancellationToken: cancellationToken); } }
private void GenerateSimulatedCompilationNonSourceEvent(Compilation compilation, AnalyzerDriver driver, bool started, CancellationToken cancellationToken) { lock (_gate) { var eventAlreadyGenerated = started ? _compilationStartGenerated : _compilationEndGenerated; if (eventAlreadyGenerated) { return; } var compilationEvent = started ? (CompilationEvent) new CompilationStartedEvent(compilation) : new CompilationCompletedEvent(compilation); var events = ImmutableArray.Create(compilationEvent); OnCompilationEventsGenerated_NoLock(events, filterTreeOpt: null, driver: driver, cancellationToken: cancellationToken); if (started) { _compilationStartGenerated = true; } else { _compilationEndGenerated = true; } } }
/// <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) { _cancellationToken = cancellationToken; _exceptionDiagnostics = new ConcurrentSet <Diagnostic>(); _driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Instance, AddExceptionDiagnostic, out _compilation, _cancellationToken); }
private void OnCompilationEventsGenerated_NoLock(ImmutableArray <CompilationEvent> compilationEvents, SyntaxTree filterTreeOpt, AnalyzerDriver driver, CancellationToken cancellationToken) { Debug.Assert(_lazyAnalyzerActionCountsMap != null); // Add the events to our global pending events map. AddToEventsMap_NoLock(compilationEvents, filterTreeOpt); // Mark the events for analysis for each analyzer. ArrayBuilder <ISymbol> newPartialSymbols = null; Debug.Assert(_pooledEventsWithAnyActionsSet.Count == 0); foreach (var kvp in _analyzerStateMap) { var analyzer = kvp.Key; var analyzerState = _analyzerStates[kvp.Value]; var actionCounts = _lazyAnalyzerActionCountsMap[analyzer]; foreach (var compilationEvent in compilationEvents) { if (HasActionsForEvent(compilationEvent, actionCounts)) { _pooledEventsWithAnyActionsSet.Add(compilationEvent); var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolDeclaredEvent?.DeclaringSyntaxReferences.Length > 1) { if (_partialSymbolsWithGeneratedSourceEvents.Contains(symbolDeclaredEvent.Symbol)) { // already processed. continue; } else { newPartialSymbols = newPartialSymbols ?? ArrayBuilder <ISymbol> .GetInstance(); newPartialSymbols.Add(symbolDeclaredEvent.Symbol); } } analyzerState.OnCompilationEventGenerated(compilationEvent, actionCounts); } } } foreach (var compilationEvent in compilationEvents) { if (!_pooledEventsWithAnyActionsSet.Remove(compilationEvent)) { // Event has no relevant actions to execute, so mark it as complete. UpdateEventsMap_NoLock(compilationEvent, add: false); } } if (newPartialSymbols != null) { _partialSymbolsWithGeneratedSourceEvents.AddAll(newPartialSymbols); newPartialSymbols.Free(); } }
private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func <AsyncQueue <CompilationEvent> > getEventQueue, int newTaskToken, CancellationToken cancellationToken) { AnalyzerDriver driver = null; Task computeTask = null; CancellationTokenSource cts; // Generate compilation events, if required. if (generateCompilationEventsOpt != null) { generateCompilationEventsOpt(); } // Populate the events cache from the generated compilation events. await PopulateEventsCacheAsync(cancellationToken).ConfigureAwait(false); try { // Get the analyzer driver to execute analysis. driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false); // Driver must have been initialized. Debug.Assert(driver.WhenInitializedTask != null); Debug.Assert(!driver.WhenInitializedTask.IsCanceled); cancellationToken.ThrowIfCancellationRequested(); // 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 suspendend; do { suspendend = false; // Create a new cancellation source to allow higher priority requests to suspend our analysis. cts = new CancellationTokenSource(); // Link the cancellation source with client supplied cancellation source, so the public API callee can also cancel analysis. using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken)) { try { // Core task to compute analyzer diagnostics. Func <Tuple <Task, CancellationTokenSource> > getComputeTask = () => Tuple.Create( Task.Run(async() => { AsyncQueue <CompilationEvent> eventQueue = null; try { // Get event queue with pending events to analyze. eventQueue = getEventQueue(); // Execute analyzer driver on the given analysis scope with the given event queue. await ComputeAnalyzerDiagnosticsCoreAsync(driver, eventQueue, analysisScope, cancellationToken: linkedCts.Token).ConfigureAwait(false); } finally { FreeEventQueue(eventQueue); } }, linkedCts.Token), cts); // Wait for higher priority tree document tasks to complete. computeTask = await SetActiveAnalysisTaskAsync(getComputeTask, analysisScope.FilterTreeOpt, newTaskToken, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); await computeTask.ConfigureAwait(false); } catch (OperationCanceledException) { cancellationToken.ThrowIfCancellationRequested(); suspendend = cts.IsCancellationRequested; } finally { cts.Dispose(); ClearExecutingTask(computeTask, analysisScope.FilterTreeOpt); computeTask = null; } } } while (suspendend); } finally { FreeDriver(driver); } }