| File: DiagnosticAnalyzer\DiagnosticQueue.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// 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.Diagnostics.CodeAnalysis; using System.Linq; namespace Microsoft.CodeAnalysis.Diagnostics { /// <summary> /// Queue to store analyzer diagnostics on the <see cref="AnalyzerDriver"/>. /// </summary> internal abstract class DiagnosticQueue { public abstract bool TryComplete(); public abstract bool TryDequeue([NotNullWhen(returnValue: true)] out Diagnostic? d); public abstract void Enqueue(Diagnostic diagnostic); // Methods specific to CategorizedDiagnosticQueue public abstract void EnqueueLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer, bool isSyntaxDiagnostic); public abstract void EnqueueNonLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer); public abstract ImmutableArray<Diagnostic> DequeueLocalSyntaxDiagnostics(DiagnosticAnalyzer analyzer); public abstract ImmutableArray<Diagnostic> DequeueLocalSemanticDiagnostics(DiagnosticAnalyzer analyzer); public abstract ImmutableArray<Diagnostic> DequeueNonLocalDiagnostics(DiagnosticAnalyzer analyzer); public static DiagnosticQueue Create(bool categorized = false) { return categorized ? (DiagnosticQueue)new CategorizedDiagnosticQueue() : new SimpleDiagnosticQueue(); } /// <summary> /// Simple diagnostics queue: maintains all diagnostics reported by all analyzers in a single queue. /// </summary> private sealed class SimpleDiagnosticQueue : DiagnosticQueue { private readonly AsyncQueue<Diagnostic> _queue; public SimpleDiagnosticQueue() { _queue = new AsyncQueue<Diagnostic>(); } public SimpleDiagnosticQueue(Diagnostic diagnostic) { _queue = new AsyncQueue<Diagnostic>(); _queue.Enqueue(diagnostic); } public override void Enqueue(Diagnostic diagnostic) { _queue.Enqueue(diagnostic); } public override void EnqueueLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer, bool isSyntaxDiagnostic) { _queue.Enqueue(diagnostic); } public override void EnqueueNonLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer) { _queue.Enqueue(diagnostic); } public override ImmutableArray<Diagnostic> DequeueLocalSemanticDiagnostics(DiagnosticAnalyzer analyzer) { throw new NotImplementedException(); } public override ImmutableArray<Diagnostic> DequeueLocalSyntaxDiagnostics(DiagnosticAnalyzer analyzer) { throw new NotImplementedException(); } public override ImmutableArray<Diagnostic> DequeueNonLocalDiagnostics(DiagnosticAnalyzer analyzer) { throw new NotImplementedException(); } public override bool TryComplete() { return _queue.TryComplete(); } public override bool TryDequeue([NotNullWhen(returnValue: true)] out Diagnostic? d) { return _queue.TryDequeue(out d); } } /// <summary> /// Categorized diagnostics queue: maintains separate set of simple diagnostic queues for local semantic, local syntax and non-local diagnostics for every analyzer. /// </summary> private sealed class CategorizedDiagnosticQueue : DiagnosticQueue { private readonly object _gate = new object(); private Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? _lazyLocalSemanticDiagnostics; private Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? _lazyLocalSyntaxDiagnostics; private Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? _lazyNonLocalDiagnostics; public override void Enqueue(Diagnostic diagnostic) { throw new InvalidOperationException(); } public override void EnqueueLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer, bool isSyntaxDiagnostic) { Debug.Assert(diagnostic.Location.Kind == LocationKind.SourceFile || diagnostic.Location.Kind == LocationKind.ExternalFile); if (isSyntaxDiagnostic) { EnqueueCore(ref _lazyLocalSyntaxDiagnostics, diagnostic, analyzer); } else { EnqueueCore(ref _lazyLocalSemanticDiagnostics, diagnostic, analyzer); } } public override void EnqueueNonLocal(Diagnostic diagnostic, DiagnosticAnalyzer analyzer) { EnqueueCore(ref _lazyNonLocalDiagnostics, diagnostic, analyzer); } private void EnqueueCore( [NotNull] ref Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? lazyDiagnosticsMap, Diagnostic diagnostic, DiagnosticAnalyzer analyzer) { lock (_gate) { lazyDiagnosticsMap ??= new Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>(); EnqueueCore_NoLock(lazyDiagnosticsMap, diagnostic, analyzer); } } private static void EnqueueCore_NoLock(Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue> diagnosticsMap, Diagnostic diagnostic, DiagnosticAnalyzer analyzer) { if (diagnosticsMap.TryGetValue(analyzer, out var queue)) { queue.Enqueue(diagnostic); } else { diagnosticsMap[analyzer] = new SimpleDiagnosticQueue(diagnostic); } } public override bool TryComplete() { return true; } public override bool TryDequeue([NotNullWhen(returnValue: true)] out Diagnostic? d) { lock (_gate) { return TryDequeue_NoLock(out d); } } private bool TryDequeue_NoLock([NotNullWhen(returnValue: true)] out Diagnostic? d) { return TryDequeue_NoLock(_lazyLocalSemanticDiagnostics, out d) || TryDequeue_NoLock(_lazyLocalSyntaxDiagnostics, out d) || TryDequeue_NoLock(_lazyNonLocalDiagnostics, out d); } private static bool TryDequeue_NoLock(Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? lazyDiagnosticsMap, [NotNullWhen(returnValue: true)] out Diagnostic? d) { Diagnostic? diag = null; if (lazyDiagnosticsMap != null && lazyDiagnosticsMap.Any(kvp => kvp.Value.TryDequeue(out diag))) { Debug.Assert(diag != null); d = diag; return true; } d = null; return false; } public override ImmutableArray<Diagnostic> DequeueLocalSyntaxDiagnostics(DiagnosticAnalyzer analyzer) { return DequeueDiagnosticsCore(analyzer, _lazyLocalSyntaxDiagnostics); } public override ImmutableArray<Diagnostic> DequeueLocalSemanticDiagnostics(DiagnosticAnalyzer analyzer) { return DequeueDiagnosticsCore(analyzer, _lazyLocalSemanticDiagnostics); } public override ImmutableArray<Diagnostic> DequeueNonLocalDiagnostics(DiagnosticAnalyzer analyzer) { return DequeueDiagnosticsCore(analyzer, _lazyNonLocalDiagnostics); } private ImmutableArray<Diagnostic> DequeueDiagnosticsCore(DiagnosticAnalyzer analyzer, Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? lazyDiagnosticsMap) { if (TryGetDiagnosticsQueue(analyzer, lazyDiagnosticsMap, out var queue)) { var builder = ImmutableArray.CreateBuilder<Diagnostic>(); while (queue.TryDequeue(out var d)) { builder.Add(d); } return builder.ToImmutable(); } return ImmutableArray<Diagnostic>.Empty; } private bool TryGetDiagnosticsQueue( DiagnosticAnalyzer analyzer, Dictionary<DiagnosticAnalyzer, SimpleDiagnosticQueue>? diagnosticsMap, [NotNullWhen(returnValue: true)] out SimpleDiagnosticQueue? queue) { queue = null; lock (_gate) { return diagnosticsMap != null && diagnosticsMap.TryGetValue(analyzer, out queue); } } } } }