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? }