Exemplo n.º 1
0
        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?
                }
            }
        }
Exemplo n.º 2
0
 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));
         }
     }
 }
Exemplo n.º 3
0
 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?
         }
     }
 }
Exemplo n.º 4
0
 public void Dispose()
 {
     CompilationEventQueue.Complete();
     DiagnosticQueue.Complete();
 }
Exemplo n.º 5
0
        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?
        }