private async Task ProcessCompilationEventsAsync(CancellationToken cancellationToken) { while (!CompilationEventQueue.IsCompleted || CompilationEventQueue.Count > 0) { CompilationEvent e; try { e = await CompilationEventQueue.DequeueAsync(cancellationToken).ConfigureAwait(false); } catch (TaskCanceledException) { // When the queue is completed with a pending DequeueAsync return then a // TaskCanceledException will be thrown. This just signals the queue is // complete and we should finish processing it. Debug.Assert(CompilationEventQueue.IsCompleted, "DequeueAsync should never throw unless the AsyncQueue<T> is completed."); break; } if (e.Compilation != _compilation) { Debug.Assert(false, "CompilationEvent with a different compilation then driver's compilation?"); continue; } try { await ProcessEventAsync(e, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { // when just a single operation is cancelled, we continue processing events. // TODO: what is the desired behavior in this case? } } }
private async Task ProcessEvents(CancellationToken cancellationToken) { while (true) { try { var e = await CompilationEventQueue.DequeueAsync(/*cancellationToken*/).ConfigureAwait(false); await ProcessEvent(e, cancellationToken).ConfigureAwait(false); } catch (TaskCanceledException) { // when the task is cancelled we stop processing events. // the caller catches this. throw; } catch (OperationCanceledException) { // when just a single operation is cancelled, we continue processing events. // TODO: what is the desired behavior in this case? } catch (Exception ex) { var desc = new DiagnosticDescriptor(DiagnosticId, CodeAnalysisResources.CompilerAnalyzerFailure, "diagnostic analyzer worker threw an exception " + ex, category: Diagnostic.CompilerDiagnosticCategory, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); addDiagnostic(Diagnostic.Create(desc, Location.None)); } } }
private async Task ProcessEvents(CancellationToken cancellationToken) { while (true) { try { var e = await CompilationEventQueue.DequeueAsync(/*cancellationToken*/); await ProcessEvent(e, cancellationToken); } catch (TaskCanceledException) { // when the task is cancelled we stop processing events. // the caller catches this. throw; } catch (OperationCanceledException) { // when just a single operation is cancelled, we continue processing events. // TODO: what is the desired behavior in this case? } } }
public void Dispose() { CompilationEventQueue.Complete(); DiagnosticQueue.Complete(); }
private async Task InitialWorker(ImmutableArray <IDiagnosticAnalyzer> analyzers, bool continueOnError, CancellationToken cancellationToken) { // Pull out the first event, which should be the "start compilation" event. var firstEvent = await CompilationEventQueue.DequeueAsync(/*cancellationToken*/).ConfigureAwait(false); var startCompilation = firstEvent as CompilationStartedEvent; if (startCompilation == null) { // The queue contents are ill formed, as they do not start with a CompilationStarted event. // Throwing an exception here won't do much good, as there is nothing higher on the call stack. // So we instead complete the queue so that the caller does not enqueue further data. CompilationEventQueue.Complete(); DiagnosticQueue.Complete(); CompilationEvent drainedEvent; while (CompilationEventQueue.TryDequeue(out drainedEvent)) { } Debug.Assert(false, "First event must be CompilationStartedEvent, not " + firstEvent.GetType().Name); } var compilation = startCompilation.Compilation; Interlocked.CompareExchange(ref this.compilation, compilation, null); // Compute the set of effective analyzers based on suppression, and running the initial analyzers var effectiveAnalyzers = GetEffectiveAnalyzers(analyzers, compilation, analyzerOptions, addDiagnostic, continueOnError, cancellationToken); ImmutableInterlocked.InterlockedInitialize(ref this.analyzers, effectiveAnalyzers); ImmutableInterlocked.InterlockedInitialize(ref declarationAnalyzersByKind, MakeDeclarationAnalyzersByKind()); ImmutableInterlocked.InterlockedInitialize(ref bodyAnalyzers, analyzers.OfType <ICodeBlockStartedAnalyzer>().ToImmutableArray()); ImmutableInterlocked.InterlockedInitialize(ref semanticModelAnalyzers, analyzers.OfType <ISemanticModelAnalyzer>().ToImmutableArray()); ImmutableInterlocked.InterlockedInitialize(ref codeBlockStartedAnalyzers, analyzers.OfType <ICodeBlockStartedAnalyzer>().ToImmutableArray()); ImmutableInterlocked.InterlockedInitialize(ref codeBlockEndedAnalyzers, analyzers.OfType <ICodeBlockEndedAnalyzer>().ToImmutableArray()); // Invoke the syntax tree analyzers // TODO: How can the caller restrict this to one or a set of trees, or a span in a tree, rather than all trees in the compilation? var syntaxAnalyzers = ArrayBuilder <Task> .GetInstance(); foreach (var tree in compilation.SyntaxTrees) { foreach (var a in analyzers.OfType <ISyntaxTreeAnalyzer>()) { var runningAsynchronously = Task.Run(() => { cancellationToken.ThrowIfCancellationRequested(); // Catch Exception from a.AnalyzeSyntaxTree ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () => { a.AnalyzeSyntaxTree(tree, addDiagnostic, analyzerOptions, cancellationToken); }); }); syntaxAnalyzers.Add(runningAsynchronously); } } ImmutableInterlocked.InterlockedInitialize(ref this.syntaxAnalyzers, syntaxAnalyzers.ToImmutableAndFree()); // start some tasks to drain the event queue cancellationToken.ThrowIfCancellationRequested(); const int nTasks = 1; var workers = ArrayBuilder <Task> .GetInstance(); for (int i = 0; i < nTasks; i++) { workers.Add(Task.Run(() => ProcessCompilationEvents(cancellationToken))); } ImmutableInterlocked.InterlockedInitialize(ref this.workers, workers.ToImmutableAndFree()); // TODO: Analyze nodes for those parts of each syntax tree that are not inside declarations that are analyzed. // For example, compilation units and namespaces, usings, etc. Perhaps those should be processed here? }