File: DiagnosticAnalyzer\AnalyzerDriver.cs
Web Access
Project: src\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.CodeDom.Compiler;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    /// <summary>
    /// Driver to execute diagnostic analyzers for a given compilation.
    /// It uses a <see cref="AsyncQueue{TElement}"/> of <see cref="CompilationEvent"/>s to drive its analysis.
    /// </summary>
    internal abstract partial class AnalyzerDriver : IDisposable
    {
        // Protect against vicious analyzers that provide large values for SymbolKind.
        private const int MaxSymbolKind = 100;
 
        // Cache delegates for static methods
        // NOTE: These methods are on hot paths and cause large allocation hit if removed.
        private static readonly Func<DiagnosticAnalyzer, bool> s_IsCompilerAnalyzerFunc = IsCompilerAnalyzer;
        private static readonly Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> s_getTopmostNodeForAnalysis = GetTopmostNodeForAnalysis;
 
        private readonly Func<SyntaxTree, CancellationToken, bool> _isGeneratedCode;
 
        /// <summary>
        /// Set of diagnostic suppressions that are suppressed via analyzer suppression actions.
        /// </summary>
        private readonly ConcurrentSet<Suppression>? _programmaticSuppressions;
 
        /// <summary>
        /// Set of diagnostics that have already been processed for application of programmatic suppressions.
        /// </summary>
        private readonly ConcurrentSet<Diagnostic>? _diagnosticsProcessedForProgrammaticSuppressions;
 
        /// <summary>
        /// Flag indicating if the <see cref="Analyzers"/> include any <see cref="DiagnosticSuppressor"/>
        /// which can suppress reported analyzer/compiler diagnostics.
        /// </summary>
        internal readonly bool HasDiagnosticSuppressors;
 
        /// <summary>
        /// Filtered diagnostic severities in the compilation, i.e. diagnostics with effective severity from this set should not be reported.
        /// PERF: If all supported diagnostics for an analyzer are from this set, we completely skip executing the analyzer.
        /// </summary>
        private readonly SeverityFilter _severityFilter;
 
        protected ImmutableArray<DiagnosticAnalyzer> Analyzers { get; }
        protected AnalyzerManager AnalyzerManager { get; }
 
        // Lazy fields/properties
        private CancellationTokenRegistration? _lazyQueueRegistration;
 
        private AnalyzerExecutor? _lazyAnalyzerExecutor;
        protected AnalyzerExecutor AnalyzerExecutor
        {
            get
            {
                Debug.Assert(_lazyAnalyzerExecutor != null);
                return _lazyAnalyzerExecutor;
            }
        }
 
        private CompilationData? _lazyCurrentCompilationData;
        protected CompilationData CurrentCompilationData
        {
            get
            {
                Debug.Assert(_lazyCurrentCompilationData != null);
                return _lazyCurrentCompilationData;
            }
        }
 
        protected CachingSemanticModelProvider SemanticModelProvider => CurrentCompilationData.SemanticModelProvider;
 
        protected ref readonly AnalyzerActions AnalyzerActions => ref _lazyAnalyzerActions;
 
        private ImmutableHashSet<DiagnosticAnalyzer>? _lazyUnsuppressedAnalyzers;
 
        /// <summary>
        /// Unsuppressed analyzers that need to be executed.
        /// </summary>
        protected ImmutableHashSet<DiagnosticAnalyzer> UnsuppressedAnalyzers
        {
            get
            {
                Debug.Assert(_lazyUnsuppressedAnalyzers != null);
                return _lazyUnsuppressedAnalyzers;
            }
        }
 
        private ConcurrentDictionary<(INamespaceOrTypeSymbol, DiagnosticAnalyzer), IGroupedAnalyzerActions>? _lazyPerSymbolAnalyzerActionsCache;
 
        /// <summary>
        /// Cache of additional analyzer actions to be executed per symbol per analyzer, which are registered in symbol start actions.
        /// We cache the tuple:
        ///   1. myActions: analyzer actions registered in the symbol start actions of containing namespace/type, which are to be executed for this symbol
        ///   2. childActions: analyzer actions registered in this symbol's start actions, which are to be executed for member symbols.
        /// </summary>
        private ConcurrentDictionary<(INamespaceOrTypeSymbol, DiagnosticAnalyzer), IGroupedAnalyzerActions> PerSymbolAnalyzerActionsCache
        {
            get
            {
                Debug.Assert(_lazyPerSymbolAnalyzerActionsCache != null);
                return _lazyPerSymbolAnalyzerActionsCache;
            }
        }
 
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>)> _lazySymbolActionsByKind;
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<SemanticModelAnalyzerAction>)> _lazySemanticModelActions;
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<SyntaxTreeAnalyzerAction>)> _lazySyntaxTreeActions;
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<AdditionalFileAnalyzerAction>)> _lazyAdditionalFileActions;
        // Compilation actions and compilation end actions have separate maps so that it is easy to
        // execute the compilation actions before the compilation end actions.
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>)> _lazyCompilationActions;
        private ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>)> _lazyCompilationEndActions;
 
        private ImmutableHashSet<DiagnosticAnalyzer>? _lazyCompilationEndAnalyzers;
        private ImmutableHashSet<DiagnosticAnalyzer> CompilationEndAnalyzers
        {
            get
            {
                Debug.Assert(_lazyCompilationEndAnalyzers != null);
                return _lazyCompilationEndAnalyzers;
            }
        }
 
        /// <summary>
        /// Default analysis mode for generated code.
        /// </summary>
        /// <remarks>
        /// This mode should always guarantee that analyzer action callbacks are enabled for generated code, i.e. <see cref="GeneratedCodeAnalysisFlags.Analyze"/> is set.
        /// However, the default diagnostic reporting mode is liable to change in future.
        /// </remarks>
        internal const GeneratedCodeAnalysisFlags DefaultGeneratedCodeAnalysisFlags = GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics;
 
        /// <summary>
        /// Map from non-concurrent analyzers to the gate guarding callback into the analyzer.
        /// </summary>
        private ImmutableSegmentedDictionary<DiagnosticAnalyzer, SemaphoreSlim> _lazyAnalyzerGateMap;
        private ImmutableSegmentedDictionary<DiagnosticAnalyzer, SemaphoreSlim> AnalyzerGateMap
        {
            get
            {
                Debug.Assert(_lazyAnalyzerGateMap != null);
                return _lazyAnalyzerGateMap;
            }
        }
 
        private ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> _lazyGeneratedCodeAnalysisFlagsMap;
 
        /// <summary>
        /// Map from analyzers to their <see cref="GeneratedCodeAnalysisFlags"/> setting.
        /// </summary>
        private ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> GeneratedCodeAnalysisFlagsMap
        {
            get
            {
                Debug.Assert(!_lazyGeneratedCodeAnalysisFlagsMap.IsDefault);
                return _lazyGeneratedCodeAnalysisFlagsMap;
            }
        }
 
        /// <summary>
        /// The set of registered analyzer actions.
        /// </summary>
        /// <seealso cref="AnalyzerActions"/>
        private AnalyzerActions _lazyAnalyzerActions;
 
        private ImmutableHashSet<DiagnosticAnalyzer>? _lazyNonConfigurableAndCustomConfigurableAnalyzers;
 
        /// <summary>
        /// Set of unsuppressed analyzers that report non-configurable or custom configurable diagnostics that cannot be suppressed with end user configuration.
        /// </summary>
        private ImmutableHashSet<DiagnosticAnalyzer> NonConfigurableAndCustomConfigurableAnalyzers
        {
            get
            {
                Debug.Assert(_lazyNonConfigurableAndCustomConfigurableAnalyzers != null);
                return _lazyNonConfigurableAndCustomConfigurableAnalyzers;
            }
        }
 
        private ImmutableHashSet<DiagnosticAnalyzer>? _lazySymbolStartAnalyzers;
 
        /// <summary>
        /// Set of analyzers that have registered symbol start analyzer actions.
        /// </summary>
        private ImmutableHashSet<DiagnosticAnalyzer> SymbolStartAnalyzers
        {
            get
            {
                Debug.Assert(_lazySymbolStartAnalyzers != null);
                return _lazySymbolStartAnalyzers;
            }
        }
 
        private bool? _lazyTreatAllCodeAsNonGeneratedCode;
 
        /// <summary>
        /// True if all analyzers need to analyze and report diagnostics in generated code - we can assume all code to be non-generated code.
        /// </summary>
        private bool TreatAllCodeAsNonGeneratedCode
        {
            get
            {
                Debug.Assert(_lazyTreatAllCodeAsNonGeneratedCode.HasValue);
                return _lazyTreatAllCodeAsNonGeneratedCode.Value;
            }
        }
 
        /// <summary>
        /// True if no analyzer needs generated code analysis - we can skip all analysis on a generated code symbol/tree.
        /// </summary>
        private bool? _lazyDoNotAnalyzeGeneratedCode;
 
        private ConcurrentDictionary<SyntaxTree, bool>? _lazyGeneratedCodeFilesMap;
 
        /// <summary>
        /// Lazily populated dictionary indicating whether a source file is a generated code file or not - we populate it lazily to avoid realizing all syntax trees in the compilation upfront.
        /// </summary>
        private ConcurrentDictionary<SyntaxTree, bool> GeneratedCodeFilesMap
        {
            get
            {
                Debug.Assert(_lazyGeneratedCodeFilesMap != null);
                return _lazyGeneratedCodeFilesMap;
            }
        }
 
        private Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>>? _lazyGeneratedCodeSymbolsForTreeMap;
 
        /// <summary>
        /// Lazily populated dictionary from tree to declared symbols with GeneratedCodeAttribute.
        /// </summary>
        private Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>> GeneratedCodeSymbolsForTreeMap
        {
            get
            {
                Debug.Assert(_lazyGeneratedCodeSymbolsForTreeMap != null);
                return _lazyGeneratedCodeSymbolsForTreeMap;
            }
        }
 
        private ConcurrentDictionary<SyntaxTree, ImmutableHashSet<DiagnosticAnalyzer>>? _lazySuppressedAnalyzersForTreeMap;
 
        /// <summary>
        /// Lazily populated dictionary from tree to analyzers that are suppressed on the entire tree.
        /// </summary>
        private ConcurrentDictionary<SyntaxTree, ImmutableHashSet<DiagnosticAnalyzer>> SuppressedAnalyzersForTreeMap
        {
            get
            {
                Debug.Assert(_lazySuppressedAnalyzersForTreeMap != null);
                return _lazySuppressedAnalyzersForTreeMap;
            }
        }
 
        private ConcurrentSet<string>? _lazySuppressedDiagnosticIdsForUnsuppressedAnalyzers;
 
        /// <summary>
        /// Lazily populated set of diagnostic IDs which are suppressed for some part of the compilation (tree/folder/entire compilation),
        /// but the analyzer reporting the diagnostic is itself not suppressed for the entire compilation, i.e. the analyzer
        /// belongs to <see cref="UnsuppressedAnalyzers"/>.
        /// </summary>
        private ConcurrentSet<string> SuppressedDiagnosticIdsForUnsuppressedAnalyzers
        {
            get
            {
                Debug.Assert(_lazySuppressedDiagnosticIdsForUnsuppressedAnalyzers != null);
                return _lazySuppressedDiagnosticIdsForUnsuppressedAnalyzers;
            }
        }
 
        private ConcurrentDictionary<ISymbol, bool>? _lazyIsGeneratedCodeSymbolMap;
 
        /// <summary>
        /// Lazily populated dictionary from symbol to a bool indicating if it is a generated code symbol.
        /// </summary>
        private ConcurrentDictionary<ISymbol, bool> IsGeneratedCodeSymbolMap
        {
            get
            {
                Debug.Assert(_lazyIsGeneratedCodeSymbolMap != null);
                return _lazyIsGeneratedCodeSymbolMap;
            }
        }
 
        /// <summary>
        /// Lazily populated dictionary indicating whether a source file has any hidden regions - we populate it lazily to avoid realizing all syntax trees in the compilation upfront.
        /// </summary>
        private ConcurrentDictionary<SyntaxTree, bool>? _lazyTreesWithHiddenRegionsMap;
 
        /// <summary>
        /// Symbol for <see cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/>.
        /// </summary>
        private INamedTypeSymbol? _lazyGeneratedCodeAttribute;
 
        /// <summary>
        /// Driver task which initializes all analyzers.
        /// This task is initialized and executed only once at start of analysis.
        /// </summary>
        private Task? _lazyInitializeTask;
 
        /// <summary>
        /// Flag to indicate if the <see cref="_lazyInitializeTask"/> was successfully started.
        /// </summary>
        private bool _initializeSucceeded = false;
 
        /// <summary>
        /// Primary driver task which processes all <see cref="CompilationEventQueue"/> events, runs analyzer actions and signals completion of <see cref="DiagnosticQueue"/> at the end.
        /// </summary>
        private Task? _lazyPrimaryTask;
 
        /// <summary>
        /// Number of worker tasks processing compilation events and executing analyzer actions.
        /// </summary>
        private readonly int _workerCount = Environment.ProcessorCount;
 
        private AsyncQueue<CompilationEvent>? _lazyCompilationEventQueue;
 
        /// <summary>
        /// Events queue for analyzer execution.
        /// </summary>
        public AsyncQueue<CompilationEvent> CompilationEventQueue
        {
            get
            {
                Debug.Assert(_lazyCompilationEventQueue != null);
                return _lazyCompilationEventQueue;
            }
        }
 
        private DiagnosticQueue? _lazyDiagnosticQueue;
 
        /// <summary>
        /// <see cref="DiagnosticQueue"/> that is fed the diagnostics as they are computed.
        /// </summary>
        public DiagnosticQueue DiagnosticQueue
        {
            get
            {
                Debug.Assert(_lazyDiagnosticQueue != null);
                return _lazyDiagnosticQueue;
            }
        }
 
        /// <summary>
        /// Create an analyzer driver.
        /// </summary>
        /// <param name="analyzers">The set of analyzers to include in the analysis</param>
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param>
        /// <param name="severityFilter">Filtered diagnostic severities in the compilation, i.e. diagnostics with effective severity from this set should not be reported.</param>
        /// <param name="isComment">Delegate to identify if the given trivia is a comment.</param>
        protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, SeverityFilter severityFilter, Func<SyntaxTrivia, bool> isComment)
        {
            Debug.Assert(!severityFilter.Contains(ReportDiagnostic.Suppress));
            Debug.Assert(!severityFilter.Contains(ReportDiagnostic.Default));
 
            this.Analyzers = analyzers;
            this.AnalyzerManager = analyzerManager;
            _isGeneratedCode = (tree, ct) => GeneratedCodeUtilities.IsGeneratedCode(tree, isComment, ct);
            _severityFilter = severityFilter;
            HasDiagnosticSuppressors = this.Analyzers.Any(static a => a is DiagnosticSuppressor);
            _programmaticSuppressions = HasDiagnosticSuppressors ? new ConcurrentSet<Suppression>() : null;
            _diagnosticsProcessedForProgrammaticSuppressions = HasDiagnosticSuppressors ? new ConcurrentSet<Diagnostic>(ReferenceEqualityComparer.Instance) : null;
            _lazyAnalyzerGateMap = ImmutableSegmentedDictionary<DiagnosticAnalyzer, SemaphoreSlim>.Empty;
        }
 
        /// <summary>
        /// Initializes the <see cref="AnalyzerActions"/> and related actions maps for the analyzer driver.
        /// It kicks off the <see cref="WhenInitializedTask"/> task for initialization.
        /// Note: This method must be invoked exactly once on the driver.
        /// </summary>
        private void Initialize(
            AnalyzerExecutor analyzerExecutor,
            DiagnosticQueue diagnosticQueue,
            CompilationData compilationData,
            AnalysisScope analysisScope,
            ConcurrentSet<string>? suppressedDiagnosticIds,
            CancellationToken cancellationToken)
        {
            try
            {
                Debug.Assert(_lazyInitializeTask == null);
 
                _lazyAnalyzerExecutor = analyzerExecutor;
                _lazyCurrentCompilationData = compilationData;
                _lazyDiagnosticQueue = diagnosticQueue;
                _lazySuppressedDiagnosticIdsForUnsuppressedAnalyzers = suppressedDiagnosticIds;
 
                // Compute the set of effective actions based on suppression, and running the initial analyzers
                _lazyInitializeTask = Task.Run(async () =>
                {
                    (_lazyAnalyzerActions, _lazyUnsuppressedAnalyzers) = await GetAnalyzerActionsAsync(Analyzers, AnalyzerManager, analyzerExecutor, analysisScope, _severityFilter, cancellationToken).ConfigureAwait(false);
                    _lazyAnalyzerGateMap = await CreateAnalyzerGateMapAsync(UnsuppressedAnalyzers, AnalyzerManager, analyzerExecutor, analysisScope, _severityFilter, cancellationToken).ConfigureAwait(false);
                    _lazyNonConfigurableAndCustomConfigurableAnalyzers = ComputeNonConfigurableAndCustomConfigurableAnalyzers(UnsuppressedAnalyzers, cancellationToken);
                    _lazySymbolStartAnalyzers = ComputeSymbolStartAnalyzers(UnsuppressedAnalyzers);
                    _lazyGeneratedCodeAnalysisFlagsMap = await CreateGeneratedCodeAnalysisFlagsMapAsync(UnsuppressedAnalyzers, AnalyzerManager, analyzerExecutor, analysisScope, _severityFilter, cancellationToken).ConfigureAwait(false);
                    _lazyTreatAllCodeAsNonGeneratedCode = ComputeShouldTreatAllCodeAsNonGeneratedCode(UnsuppressedAnalyzers, GeneratedCodeAnalysisFlagsMap);
                    _lazyDoNotAnalyzeGeneratedCode = ComputeShouldSkipAnalysisOnGeneratedCode(UnsuppressedAnalyzers, GeneratedCodeAnalysisFlagsMap, TreatAllCodeAsNonGeneratedCode);
                    _lazyGeneratedCodeFilesMap = new ConcurrentDictionary<SyntaxTree, bool>();
                    _lazyGeneratedCodeSymbolsForTreeMap = new Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>>();
                    _lazyIsGeneratedCodeSymbolMap = new ConcurrentDictionary<ISymbol, bool>();
                    _lazyTreesWithHiddenRegionsMap = new ConcurrentDictionary<SyntaxTree, bool>();
                    _lazySuppressedAnalyzersForTreeMap = new ConcurrentDictionary<SyntaxTree, ImmutableHashSet<DiagnosticAnalyzer>>();
                    _lazyGeneratedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute");
 
                    _lazySymbolActionsByKind = MakeSymbolActionsByKind(in AnalyzerActions);
                    _lazySemanticModelActions = MakeActionsByAnalyzer(AnalyzerActions.SemanticModelActions);
                    _lazySyntaxTreeActions = MakeActionsByAnalyzer(AnalyzerActions.SyntaxTreeActions);
                    _lazyAdditionalFileActions = MakeActionsByAnalyzer(AnalyzerActions.AdditionalFileActions);
                    _lazyCompilationActions = MakeActionsByAnalyzer(this.AnalyzerActions.CompilationActions);
                    _lazyCompilationEndActions = MakeActionsByAnalyzer(this.AnalyzerActions.CompilationEndActions);
                    _lazyCompilationEndAnalyzers = MakeCompilationEndAnalyzers(_lazyCompilationEndActions);
 
                    if (this.AnalyzerActions.SymbolStartActionsCount > 0)
                    {
                        _lazyPerSymbolAnalyzerActionsCache = new ConcurrentDictionary<(INamespaceOrTypeSymbol, DiagnosticAnalyzer), IGroupedAnalyzerActions>();
                    }
 
                }, cancellationToken);
 
                // create the primary driver task.
                cancellationToken.ThrowIfCancellationRequested();
 
                _initializeSucceeded = true;
            }
            finally
            {
                if (_lazyInitializeTask == null)
                {
                    // Set initializeTask to be a cancelled task.
                    _lazyInitializeTask = Task.FromCanceled(new CancellationToken(canceled: true));
 
                    // Set primaryTask to be a cancelled task.
                    _lazyPrimaryTask = Task.FromCanceled(new CancellationToken(canceled: true));
 
                    // Try to set the DiagnosticQueue to be complete.
                    this.DiagnosticQueue.TryComplete();
                }
            }
        }
 
        internal void Initialize(
           Compilation compilation,
           CompilationWithAnalyzersOptions analysisOptions,
           CompilationData compilationData,
           AnalysisScope analysisScope,
           bool categorizeDiagnostics,
           bool trackSuppressedDiagnosticIds,
           CancellationToken cancellationToken)
        {
            Debug.Assert(_lazyInitializeTask == null);
            Debug.Assert(compilation.SemanticModelProvider != null);
 
            var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics);
            var suppressedDiagnosticIds = trackSuppressedDiagnosticIds ? new ConcurrentSet<string>() : null;
 
            Action<Diagnostic, CancellationToken>? addNotCategorizedDiagnostic = null;
            Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? addCategorizedLocalDiagnostic = null;
            Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? addCategorizedNonLocalDiagnostic = null;
            if (categorizeDiagnostics)
            {
                addCategorizedLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds);
                addCategorizedNonLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds);
            }
            else
            {
                addNotCategorizedDiagnostic = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds);
            }
 
            // Wrap onAnalyzerException to pass in filtered diagnostic.
            Action<Exception, DiagnosticAnalyzer, Diagnostic, CancellationToken> newOnAnalyzerException = (ex, analyzer, diagnostic, cancellationToken) =>
            {
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds, cancellationToken);
                if (filteredDiagnostic != null)
                {
                    if (analysisOptions.OnAnalyzerException != null)
                    {
                        analysisOptions.OnAnalyzerException(ex, analyzer, filteredDiagnostic);
                    }
                    else if (categorizeDiagnostics)
                    {
                        addCategorizedNonLocalDiagnostic!(filteredDiagnostic, analyzer, cancellationToken);
                    }
                    else
                    {
                        addNotCategorizedDiagnostic!(filteredDiagnostic, cancellationToken);
                    }
                }
            };
 
            var analyzerExecutor = AnalyzerExecutor.Create(
                compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter,
                IsCompilerAnalyzer, AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate,
                getSemanticModel: GetOrCreateSemanticModel, _severityFilter,
                analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, s => _programmaticSuppressions!.Add(s));
 
            Initialize(analyzerExecutor, diagnosticQueue, compilationData, analysisScope, suppressedDiagnosticIds, cancellationToken);
        }
 
        private SemaphoreSlim? GetAnalyzerGate(DiagnosticAnalyzer analyzer)
        {
            if (AnalyzerGateMap.TryGetValue(analyzer, out var gate))
            {
                // Non-concurrent analyzer, needs all the callbacks guarded by a gate.
                return gate;
            }
 
            // Concurrent analyzer.
            return null;
        }
 
        private ImmutableHashSet<DiagnosticAnalyzer> ComputeNonConfigurableAndCustomConfigurableAnalyzers(ImmutableHashSet<DiagnosticAnalyzer> unsuppressedAnalyzers, CancellationToken cancellationToken)
        {
            var builder = ImmutableHashSet.CreateBuilder<DiagnosticAnalyzer>();
            foreach (var analyzer in unsuppressedAnalyzers)
            {
                var descriptors = AnalyzerManager.GetSupportedDiagnosticDescriptors(analyzer, AnalyzerExecutor, cancellationToken);
                foreach (var descriptor in descriptors)
                {
                    if (descriptor.IsNotConfigurable() || descriptor.IsCustomSeverityConfigurable())
                    {
                        builder.Add(analyzer);
                        break;
                    }
                }
            }
 
            return builder.ToImmutableHashSet();
        }
 
        private ImmutableHashSet<DiagnosticAnalyzer> ComputeSymbolStartAnalyzers(ImmutableHashSet<DiagnosticAnalyzer> unsuppressedAnalyzers)
        {
            var builder = ImmutableHashSet.CreateBuilder<DiagnosticAnalyzer>();
            foreach (var action in this.AnalyzerActions.SymbolStartActions)
            {
                if (unsuppressedAnalyzers.Contains(action.Analyzer))
                {
                    builder.Add(action.Analyzer);
                }
            }
 
            return builder.ToImmutableHashSet();
        }
 
        private static bool ComputeShouldSkipAnalysisOnGeneratedCode(
            ImmutableHashSet<DiagnosticAnalyzer> analyzers,
            ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> generatedCodeAnalysisFlagsMap,
            bool treatAllCodeAsNonGeneratedCode)
        {
            foreach (var analyzer in analyzers)
            {
                if (!ShouldSkipAnalysisOnGeneratedCode(analyzer, generatedCodeAnalysisFlagsMap, treatAllCodeAsNonGeneratedCode))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        /// <summary>
        /// Returns true if all analyzers need to analyze and report diagnostics in generated code - we can assume all code to be non-generated code.
        /// </summary>
        private static bool ComputeShouldTreatAllCodeAsNonGeneratedCode(ImmutableHashSet<DiagnosticAnalyzer> analyzers, ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> generatedCodeAnalysisFlagsMap)
        {
            foreach (var analyzer in analyzers)
            {
                var flags = generatedCodeAnalysisFlagsMap[analyzer];
                var analyze = (flags & GeneratedCodeAnalysisFlags.Analyze) != 0;
                var report = (flags & GeneratedCodeAnalysisFlags.ReportDiagnostics) != 0;
                if (!analyze || !report)
                {
                    return false;
                }
            }
 
            return true;
        }
 
        private bool ShouldSkipAnalysisOnGeneratedCode(DiagnosticAnalyzer analyzer)
            => ShouldSkipAnalysisOnGeneratedCode(analyzer, GeneratedCodeAnalysisFlagsMap, TreatAllCodeAsNonGeneratedCode);
 
        private static bool ShouldSkipAnalysisOnGeneratedCode(
            DiagnosticAnalyzer analyzer,
            ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> generatedCodeAnalysisFlagsMap,
            bool treatAllCodeAsNonGeneratedCode)
        {
            if (treatAllCodeAsNonGeneratedCode)
            {
                return false;
            }
 
            var mode = generatedCodeAnalysisFlagsMap[analyzer];
            return (mode & GeneratedCodeAnalysisFlags.Analyze) == 0;
        }
 
        private bool ShouldSuppressGeneratedCodeDiagnostic(Diagnostic diagnostic, DiagnosticAnalyzer analyzer, Compilation compilation, CancellationToken cancellationToken)
        {
            if (TreatAllCodeAsNonGeneratedCode)
            {
                return false;
            }
 
            var generatedCodeAnalysisFlags = GeneratedCodeAnalysisFlagsMap[analyzer];
            var suppressInGeneratedCode = (generatedCodeAnalysisFlags & GeneratedCodeAnalysisFlags.ReportDiagnostics) == 0;
            return suppressInGeneratedCode && IsInGeneratedCode(diagnostic.Location, compilation, cancellationToken);
        }
 
        /// <summary>
        /// Attaches a pre-populated event queue to the driver and processes all events in the queue.
        /// </summary>
        /// <param name="eventQueue">Compilation events to analyze.</param>
        /// <param name="analysisScope">Scope of analysis.</param>
        /// <param name="cancellationToken">Cancellation token to abort analysis.</param>
        /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CompilationData, AnalysisScope, ConcurrentSet{string}, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks>
        internal async Task AttachQueueAndProcessAllEventsAsync(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            try
            {
                if (_initializeSucceeded)
                {
                    _lazyCompilationEventQueue = eventQueue;
                    _lazyQueueRegistration = default(CancellationTokenRegistration);
 
                    await ExecutePrimaryAnalysisTaskAsync(analysisScope, usingPrePopulatedEventQueue: true, cancellationToken).ConfigureAwait(false);
 
                    _lazyPrimaryTask = Task.FromResult(true);
                }
            }
            finally
            {
                // Set primaryTask to be a cancelled task.
                _lazyPrimaryTask ??= Task.FromCanceled(new CancellationToken(canceled: true));
            }
        }
 
        /// <summary>
        /// Attaches event queue to the driver and start processing all events pertaining to the given analysis scope.
        /// </summary>
        /// <param name="eventQueue">Compilation events to analyze.</param>
        /// <param name="analysisScope">Scope of analysis.</param>
        /// <param name="usingPrePopulatedEventQueue">Boolean flag indicating whether we should only process the already populated events or wait for <see cref="CompilationCompletedEvent"/>.</param>
        /// <param name="cancellationToken">Cancellation token to abort analysis.</param>
        /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CompilationData, AnalysisScope, ConcurrentSet{string}, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks>
        internal void AttachQueueAndStartProcessingEvents(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, bool usingPrePopulatedEventQueue, CancellationToken cancellationToken)
        {
            try
            {
                if (_initializeSucceeded)
                {
                    _lazyCompilationEventQueue = eventQueue;
                    _lazyQueueRegistration = cancellationToken.Register(() =>
                    {
                        this.CompilationEventQueue.TryComplete();
                        this.DiagnosticQueue.TryComplete();
                    });
 
                    _lazyPrimaryTask = ExecutePrimaryAnalysisTaskAsync(analysisScope, usingPrePopulatedEventQueue, cancellationToken)
                        .ContinueWith(c => DiagnosticQueue.TryComplete(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                }
            }
            finally
            {
                if (_lazyPrimaryTask == null)
                {
                    // Set primaryTask to be a cancelled task.
                    _lazyPrimaryTask = Task.FromCanceled(new CancellationToken(canceled: true));
 
                    // Try to set the DiagnosticQueue to be complete.
                    this.DiagnosticQueue.TryComplete();
                }
            }
        }
 
        private async Task ExecutePrimaryAnalysisTaskAsync(AnalysisScope analysisScope, bool usingPrePopulatedEventQueue, CancellationToken cancellationToken)
        {
            Debug.Assert(analysisScope != null);
 
            await WhenInitializedTask.ConfigureAwait(false);
 
            if (WhenInitializedTask.IsFaulted)
            {
                OnDriverException(WhenInitializedTask, this.AnalyzerExecutor, analysisScope.Analyzers, cancellationToken);
            }
            else if (!WhenInitializedTask.IsCanceled)
            {
                await ProcessCompilationEventsAsync(analysisScope, usingPrePopulatedEventQueue, cancellationToken).ConfigureAwait(false);
 
                // If not using pre-populated event queue (batch mode), then verify all symbol end actions were processed.
                if (!usingPrePopulatedEventQueue)
                {
                    AnalyzerManager.VerifyAllSymbolEndActionsExecuted();
                }
            }
        }
 
        private static void OnDriverException(Task faultedTask, AnalyzerExecutor analyzerExecutor, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
        {
            Debug.Assert(faultedTask.IsFaulted);
 
            var innerException = faultedTask.Exception?.InnerException;
            if (innerException == null || innerException is OperationCanceledException)
            {
                return;
            }
 
            var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(innerException);
 
            // Just pick the first analyzer from the scope for the onAnalyzerException callback.
            // The exception diagnostic's message and description will not include the analyzer, but explicitly state its a driver exception.
            var analyzer = analyzers[0];
 
            analyzerExecutor.OnAnalyzerException(innerException, analyzer, diagnostic, cancellationToken);
        }
 
        private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            if (analysisScope.IsSingleFileAnalysis && !analysisScope.IsSyntacticSingleFileAnalysis)
            {
                // For partial analysis, only execute syntax tree actions if performing syntax analysis.
                return;
            }
 
            foreach (var tree in analysisScope.SyntaxTrees)
            {
                var isGeneratedCode = IsGeneratedCode(tree, cancellationToken);
                var file = new SourceOrAdditionalFile(tree);
                if (isGeneratedCode && DoNotAnalyzeGeneratedCode)
                {
                    continue;
                }
 
                foreach (var (analyzer, syntaxTreeActions) in _lazySyntaxTreeActions)
                {
                    if (!analysisScope.Contains(analyzer))
                    {
                        continue;
                    }
 
                    cancellationToken.ThrowIfCancellationRequested();
 
                    // Execute actions for a given analyzer sequentially.
                    AnalyzerExecutor.ExecuteSyntaxTreeActions(syntaxTreeActions, analyzer, file, analysisScope.FilterSpanOpt, isGeneratedCode, cancellationToken);
                }
            }
        }
 
        private void ExecuteAdditionalFileActions(AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            if (analysisScope.IsSingleFileAnalysis && !analysisScope.IsSyntacticSingleFileAnalysis)
            {
                // For partial analysis, only execute additional file actions if performing syntactic single file analysis.
                return;
            }
 
            foreach (var additionalFile in analysisScope.AdditionalFiles)
            {
                var file = new SourceOrAdditionalFile(additionalFile);
 
                foreach (var (analyzer, additionalFileActions) in _lazyAdditionalFileActions)
                {
                    if (!analysisScope.Contains(analyzer))
                    {
                        continue;
                    }
 
                    cancellationToken.ThrowIfCancellationRequested();
 
                    // Execute actions for a given analyzer sequentially.
                    AnalyzerExecutor.ExecuteAdditionalFileActions(additionalFileActions, analyzer, file, analysisScope.FilterSpanOpt, cancellationToken);
                }
            }
        }
 
        /// <summary>
        /// Create an <see cref="AnalyzerDriver"/> and attach it to the given compilation.
        /// </summary>
        /// <param name="compilation">The compilation to which the new driver should be attached.</param>
        /// <param name="analyzers">The set of analyzers to include in the analysis.</param>
        /// <param name="options">Options that are passed to analyzers.</param>
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
        /// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param>
        /// <param name="reportAnalyzer">Report additional information related to analyzers, such as analyzer execution time.</param>
        /// <param name="severityFilter">Filtered diagnostic severities in the compilation, i.e. diagnostics with effective severity from this set should not be reported.</param>
        /// <param name="trackSuppressedDiagnosticIds">Track diagnostic ids which are suppressed through options.</param>
        /// <param name="newCompilation">The new compilation with the analyzer driver attached.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
        /// <returns>A newly created analyzer driver</returns>
        /// <remarks>
        /// Note that since a compilation is immutable, the act of creating a driver and attaching it produces
        /// a new compilation. Any further actions on the compilation should use the new compilation.
        /// </remarks>
        public static AnalyzerDriver CreateAndAttachToCompilation(
            Compilation compilation,
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerOptions options,
            AnalyzerManager analyzerManager,
            Action<Diagnostic> addExceptionDiagnostic,
            bool reportAnalyzer,
            SeverityFilter severityFilter,
            bool trackSuppressedDiagnosticIds,
            out Compilation newCompilation,
            CancellationToken cancellationToken)
        {
            Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException =
                (ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic);
 
            Func<Exception, bool>? nullFilter = null;
            return CreateAndAttachToCompilation(compilation, analyzers, options, analyzerManager, onAnalyzerException, nullFilter, reportAnalyzer, severityFilter, trackSuppressedDiagnosticIds, out newCompilation, cancellationToken: cancellationToken);
        }
 
        // internal for testing purposes
        internal static AnalyzerDriver CreateAndAttachToCompilation(
            Compilation compilation,
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerOptions options,
            AnalyzerManager analyzerManager,
            Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
            Func<Exception, bool>? analyzerExceptionFilter,
            bool reportAnalyzer,
            SeverityFilter severityFilter,
            bool trackSuppressedDiagnosticIds,
            out Compilation newCompilation,
            CancellationToken cancellationToken)
        {
            AnalyzerDriver analyzerDriver = compilation.CreateAnalyzerDriver(analyzers, analyzerManager, severityFilter);
            newCompilation = compilation
                .WithSemanticModelProvider(new CachingSemanticModelProvider())
                .WithEventQueue(new AsyncQueue<CompilationEvent>());
 
            var categorizeDiagnostics = false;
            var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, analyzerExceptionFilter: analyzerExceptionFilter, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer, reportSuppressedDiagnostics: false);
            var analysisScope = AnalysisScope.CreateForBatchCompile(newCompilation, options, analyzers);
            analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), analysisScope, categorizeDiagnostics, trackSuppressedDiagnosticIds, cancellationToken);
 
            analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken);
            return analyzerDriver;
        }
 
        /// <summary>
        /// Returns all diagnostics computed by the analyzers since the last time this was invoked.
        /// If <see cref="CompilationEventQueue"/> has been completed with all compilation events, then it waits for
        /// <see cref="WhenCompletedTask"/> task for the driver to finish processing all events and generate remaining analyzer diagnostics.
        /// </summary>
        public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync(Compilation compilation, CancellationToken cancellationToken)
        {
            var allDiagnostics = DiagnosticBag.GetInstance();
            if (CompilationEventQueue.IsCompleted)
            {
                await this.WhenCompletedTask.ConfigureAwait(false);
 
                if (this.WhenCompletedTask.IsFaulted)
                {
                    OnDriverException(this.WhenCompletedTask, this.AnalyzerExecutor, this.Analyzers, cancellationToken);
                }
            }
 
            var suppressMessageState = CurrentCompilationData.SuppressMessageAttributeState;
            var reportSuppressedDiagnostics = compilation.Options.ReportSuppressedDiagnostics;
            while (DiagnosticQueue.TryDequeue(out var diagnostic))
            {
                diagnostic = suppressMessageState.ApplySourceSuppressions(diagnostic);
                if (reportSuppressedDiagnostics || !diagnostic.IsSuppressed)
                {
                    allDiagnostics.Add(diagnostic);
                }
            }
 
            return allDiagnostics.ToReadOnlyAndFree();
        }
 
        /// <summary>
        /// Returns an array of  <see cref="DiagnosticDescriptor"/>s for all <see cref="Analyzers"/>
        /// along with <see cref="DiagnosticDescriptorErrorLoggerInfo"/> to be logged by the <see cref="ErrorLogger"/>.
        /// </summary>
        public ImmutableArray<(DiagnosticDescriptor Descriptor, DiagnosticDescriptorErrorLoggerInfo Info)> GetAllDiagnosticDescriptorsWithInfo(CancellationToken cancellationToken, out double totalAnalyzerExecutionTime)
        {
            var uniqueDiagnosticIds = PooledHashSet<string>.GetInstance();
            var analyzersSuppressedForSomeTree = SuppressedAnalyzersForTreeMap.SelectMany(kvp => kvp.Value).ToImmutableHashSet();
            totalAnalyzerExecutionTime = AnalyzerExecutionTimes.Sum(kvp => kvp.Value.TotalSeconds);
 
            var builder = ArrayBuilder<(DiagnosticDescriptor Descriptor, DiagnosticDescriptorErrorLoggerInfo Info)>.GetInstance();
            foreach (var analyzer in Analyzers)
            {
                var descriptors = AnalyzerManager.GetSupportedDiagnosticDescriptors(analyzer, AnalyzerExecutor, cancellationToken);
 
                // Check if this analyzer is suppressed for the entire compilation via global project level options
                // or for one or more syntax trees via editorconfig.
                bool isAnalyzerEverSuppressed = !UnsuppressedAnalyzers.Contains(analyzer) ||
                    analyzersSuppressedForSomeTree.Contains(analyzer);
 
                double analyzerExecutionTime = 0;
                if (AnalyzerExecutionTimes.TryGetValue(analyzer, out var analyzerExecutionTimeSpan))
                {
                    analyzerExecutionTime = analyzerExecutionTimeSpan.TotalSeconds;
                }
 
                var executionPercentage = (int)(analyzerExecutionTime * 100 / totalAnalyzerExecutionTime);
 
                foreach (var descriptor in descriptors)
                {
                    if (!uniqueDiagnosticIds.Add(descriptor.Id))
                        continue;
 
                    // Analyzers which were executed for the entire compilation might report
                    // multiple diagnostic IDs, such that a strict subset of those ids are suppressed
                    // for the entire compilation or for one or more syntax trees.
                    // We want to report these diagnostic IDs as suppressed.
                    var isDiagnosticIdEverSuppressed = isAnalyzerEverSuppressed ||
                        SuppressedDiagnosticIdsForUnsuppressedAnalyzers.Contains(descriptor.Id);
 
                    var effectiveSeverities = GetEffectiveSeverities(descriptor, AnalyzerExecutor.Compilation, AnalyzerExecutor.AnalyzerOptions, cancellationToken);
                    var info = new DiagnosticDescriptorErrorLoggerInfo(analyzerExecutionTime, executionPercentage, effectiveSeverities, isDiagnosticIdEverSuppressed);
                    builder.Add((descriptor, info));
                }
            }
 
            uniqueDiagnosticIds.Free();
            return builder.ToImmutableAndFree();
 
            static ImmutableHashSet<ReportDiagnostic> GetEffectiveSeverities(
                DiagnosticDescriptor descriptor,
                Compilation compilation,
                AnalyzerOptions analyzerOptions,
                CancellationToken cancellationToken)
            {
                var defaultSeverity = descriptor.IsEnabledByDefault ?
                    DiagnosticDescriptor.MapSeverityToReport(descriptor.DefaultSeverity) :
                    ReportDiagnostic.Suppress;
 
                if (descriptor.IsNotConfigurable())
                    return ImmutableHashSet.Create(defaultSeverity);
 
                if (compilation.Options.SpecificDiagnosticOptions.TryGetValue(descriptor.Id, out var severity) ||
                    compilation.Options.SyntaxTreeOptionsProvider?.TryGetGlobalDiagnosticValue(descriptor.Id, cancellationToken, out severity) == true)
                {
                    if (severity != ReportDiagnostic.Default)
                    {
                        defaultSeverity = severity;
                    }
                }
 
                // Handle /warnaserror
                if (defaultSeverity == ReportDiagnostic.Warn &&
                    compilation.Options.GeneralDiagnosticOption == ReportDiagnostic.Error)
                {
                    defaultSeverity = ReportDiagnostic.Error;
                }
 
                if (compilation.Options.SyntaxTreeOptionsProvider is not { } syntaxTreeProvider ||
                    compilation.SyntaxTrees.IsEmpty())
                {
                    return ImmutableHashSet.Create(defaultSeverity);
                }
 
                var builder = ImmutableHashSet.CreateBuilder<ReportDiagnostic>();
                foreach (var tree in compilation.SyntaxTrees)
                {
                    var severityForTree = defaultSeverity;
 
                    if (syntaxTreeProvider.TryGetDiagnosticValue(tree, descriptor.Id, cancellationToken, out severity) ||
                        analyzerOptions.TryGetSeverityFromBulkConfiguration(tree, compilation, descriptor, cancellationToken, out severity))
                    {
                        Debug.Assert(severity != ReportDiagnostic.Default);
 
                        // Handle /warnaserror
                        if (severity == ReportDiagnostic.Warn &&
                            compilation.Options.GeneralDiagnosticOption == ReportDiagnostic.Error)
                        {
                            severity = ReportDiagnostic.Error;
                        }
 
                        severityForTree = severity;
                    }
 
                    builder.Add(severityForTree);
                }
 
                return builder.ToImmutable();
            }
        }
 
        private SemanticModel GetOrCreateSemanticModel(SyntaxTree tree)
            => GetOrCreateSemanticModel(tree, AnalyzerExecutor.Compilation);
 
        protected SemanticModel GetOrCreateSemanticModel(SyntaxTree tree, Compilation compilation)
        {
            Debug.Assert(compilation.ContainsSyntaxTree(tree));
 
            return SemanticModelProvider.GetSemanticModel(tree, compilation);
        }
 
        public void ApplyProgrammaticSuppressions(DiagnosticBag reportedDiagnostics, Compilation compilation, CancellationToken cancellationToken)
        {
            Debug.Assert(!reportedDiagnostics.IsEmptyWithoutResolution);
            if (!HasDiagnosticSuppressors)
            {
                return;
            }
 
            var newDiagnostics = ApplyProgrammaticSuppressionsCore(reportedDiagnostics.ToReadOnly(), compilation, cancellationToken);
            reportedDiagnostics.Clear();
            reportedDiagnostics.AddRange(newDiagnostics);
        }
 
        public ImmutableArray<Diagnostic> ApplyProgrammaticSuppressions(ImmutableArray<Diagnostic> reportedDiagnostics, Compilation compilation, CancellationToken cancellationToken)
        {
            if (reportedDiagnostics.IsEmpty ||
                !HasDiagnosticSuppressors)
            {
                return reportedDiagnostics;
            }
 
            return ApplyProgrammaticSuppressionsCore(reportedDiagnostics, compilation, cancellationToken);
        }
 
        private ImmutableArray<Diagnostic> ApplyProgrammaticSuppressionsCore(ImmutableArray<Diagnostic> reportedDiagnostics, Compilation compilation, CancellationToken cancellationToken)
        {
            Debug.Assert(HasDiagnosticSuppressors);
            Debug.Assert(!reportedDiagnostics.IsEmpty);
            Debug.Assert(_programmaticSuppressions != null);
            Debug.Assert(_diagnosticsProcessedForProgrammaticSuppressions != null);
 
            try
            {
                // We do not allow analyzer based suppressions for following category of diagnostics:
                //  1. Diagnostics which are already suppressed in source via pragma/suppress message attribute.
                //  2. Diagnostics explicitly tagged as not configurable by analyzer authors - this includes compiler error diagnostics.
                //  3. Diagnostics which are marked as error by default by diagnostic authors.
                var suppressableDiagnostics = reportedDiagnostics.Where(d => !d.IsSuppressed &&
                                                                             !d.IsNotConfigurable() &&
                                                                             d.DefaultSeverity != DiagnosticSeverity.Error &&
                                                                             !_diagnosticsProcessedForProgrammaticSuppressions.Contains(d));
 
                if (suppressableDiagnostics.IsEmpty())
                {
                    return reportedDiagnostics;
                }
 
                executeSuppressionActions(suppressableDiagnostics, concurrent: compilation.Options.ConcurrentBuild);
                if (_programmaticSuppressions.IsEmpty)
                {
                    return reportedDiagnostics;
                }
 
                var builder = ArrayBuilder<Diagnostic>.GetInstance(reportedDiagnostics.Length);
                ImmutableDictionary<Diagnostic, ProgrammaticSuppressionInfo> programmaticSuppressionsByDiagnostic = createProgrammaticSuppressionsByDiagnosticMap(_programmaticSuppressions);
                foreach (var diagnostic in reportedDiagnostics)
                {
                    if (programmaticSuppressionsByDiagnostic.TryGetValue(diagnostic, out var programmaticSuppressionInfo))
                    {
                        Debug.Assert(suppressableDiagnostics.Contains(diagnostic));
                        Debug.Assert(!diagnostic.IsSuppressed);
                        var suppressedDiagnostic = diagnostic.WithProgrammaticSuppression(programmaticSuppressionInfo);
                        Debug.Assert(suppressedDiagnostic.IsSuppressed);
                        builder.Add(suppressedDiagnostic);
                    }
                    else
                    {
                        builder.Add(diagnostic);
                    }
                }
 
                return builder.ToImmutableAndFree();
            }
            finally
            {
                // Mark the reported diagnostics as processed for programmatic suppressions to avoid duplicate callbacks to suppressors for same diagnostics.
                _diagnosticsProcessedForProgrammaticSuppressions.AddRange(reportedDiagnostics);
            }
 
            void executeSuppressionActions(IEnumerable<Diagnostic> reportedDiagnostics, bool concurrent)
            {
                var suppressors = this.Analyzers.OfType<DiagnosticSuppressor>();
                if (concurrent)
                {
                    // Kick off tasks to concurrently execute suppressors.
                    // Note that we avoid using Parallel.ForEach here to avoid wrapped exceptions.
                    // See https://github.com/dotnet/roslyn/issues/41713 for details.
                    var tasks = ArrayBuilder<Task>.GetInstance();
                    try
                    {
                        foreach (var suppressor in suppressors)
                        {
                            var suppressableDiagnostics = getSuppressableDiagnostics(suppressor);
                            if (!suppressableDiagnostics.IsEmpty)
                            {
                                var task = Task.Run(
                                    () => AnalyzerExecutor.ExecuteSuppressionAction(suppressor, suppressableDiagnostics, cancellationToken),
                                    cancellationToken);
                                tasks.Add(task);
                            }
                        }
 
                        Task.WaitAll(tasks.ToArray(), cancellationToken);
                    }
                    finally
                    {
                        tasks.Free();
                    }
                }
                else
                {
                    foreach (var suppressor in suppressors)
                    {
                        AnalyzerExecutor.ExecuteSuppressionAction(suppressor, getSuppressableDiagnostics(suppressor), cancellationToken);
                    }
                }
 
                return;
 
                ImmutableArray<Diagnostic> getSuppressableDiagnostics(DiagnosticSuppressor suppressor)
                {
                    var supportedSuppressions = AnalyzerManager.GetSupportedSuppressionDescriptors(suppressor, AnalyzerExecutor, cancellationToken);
                    if (supportedSuppressions.IsEmpty)
                    {
                        return ImmutableArray<Diagnostic>.Empty;
                    }
 
                    using var builder = TemporaryArray<Diagnostic>.Empty;
                    foreach (var diagnostic in reportedDiagnostics)
                    {
                        if (supportedSuppressions.Contains(s => s.SuppressedDiagnosticId == diagnostic.Id))
                        {
                            builder.Add(diagnostic);
                        }
                    }
 
                    return builder.ToImmutableAndClear();
                }
            }
 
            static ImmutableDictionary<Diagnostic, ProgrammaticSuppressionInfo> createProgrammaticSuppressionsByDiagnosticMap(ConcurrentSet<Suppression> programmaticSuppressions)
            {
                var programmaticSuppressionsBuilder = PooledDictionary<Diagnostic, ArrayBuilder<Suppression>>.GetInstance();
 
                foreach (var programmaticSuppression in programmaticSuppressions)
                {
                    if (!programmaticSuppressionsBuilder.TryGetValue(programmaticSuppression.SuppressedDiagnostic, out var array))
                    {
                        array = ArrayBuilder<Suppression>.GetInstance();
                        programmaticSuppressionsBuilder.Add(programmaticSuppression.SuppressedDiagnostic, array);
                    }
 
                    if (!array.Contains(programmaticSuppression))
                        array.Add(programmaticSuppression);
                }
 
                var mapBuilder = ImmutableDictionary.CreateBuilder<Diagnostic, ProgrammaticSuppressionInfo>();
                foreach (var (diagnostic, set) in programmaticSuppressionsBuilder)
                {
                    mapBuilder.Add(diagnostic, new ProgrammaticSuppressionInfo(set.ToImmutableAndFree()));
                }
 
                programmaticSuppressionsBuilder.Free();
                return mapBuilder.ToImmutable();
            }
        }
 
        public ImmutableArray<Diagnostic> DequeueLocalDiagnosticsAndApplySuppressions(DiagnosticAnalyzer analyzer, bool syntax, Compilation compilation, CancellationToken cancellationToken)
        {
            var diagnostics = syntax ? DiagnosticQueue.DequeueLocalSyntaxDiagnostics(analyzer) : DiagnosticQueue.DequeueLocalSemanticDiagnostics(analyzer);
            return FilterDiagnosticsSuppressedInSourceOrByAnalyzers(diagnostics, compilation, cancellationToken);
        }
 
        public ImmutableArray<Diagnostic> DequeueNonLocalDiagnosticsAndApplySuppressions(DiagnosticAnalyzer analyzer, Compilation compilation, CancellationToken cancellationToken)
        {
            var diagnostics = DiagnosticQueue.DequeueNonLocalDiagnostics(analyzer);
            return FilterDiagnosticsSuppressedInSourceOrByAnalyzers(diagnostics, compilation, cancellationToken);
        }
 
        private ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSourceOrByAnalyzers(ImmutableArray<Diagnostic> diagnostics, Compilation compilation, CancellationToken cancellationToken)
        {
            diagnostics = FilterDiagnosticsSuppressedInSource(diagnostics, compilation, CurrentCompilationData.SuppressMessageAttributeState);
            return ApplyProgrammaticSuppressionsAndFilterDiagnostics(diagnostics, compilation, cancellationToken);
        }
 
        private static ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSource(
            ImmutableArray<Diagnostic> diagnostics,
            Compilation compilation,
            SuppressMessageAttributeState suppressMessageState)
        {
            if (diagnostics.IsEmpty)
            {
                return diagnostics;
            }
 
            var reportSuppressedDiagnostics = compilation.Options.ReportSuppressedDiagnostics;
            var builder = ImmutableArray.CreateBuilder<Diagnostic>();
            for (var i = 0; i < diagnostics.Length; i++)
            {
#if DEBUG
                // We should have ignored diagnostics with invalid locations and reported analyzer exception diagnostic for the same.
                DiagnosticAnalysisContextHelpers.VerifyDiagnosticLocationsInCompilation(diagnostics[i], compilation);
#endif
 
                var diagnostic = suppressMessageState.ApplySourceSuppressions(diagnostics[i]);
                if (!reportSuppressedDiagnostics && diagnostic.IsSuppressed)
                {
                    // Diagnostic suppressed in source.
                    continue;
                }
 
                builder.Add(diagnostic);
            }
 
            return builder.ToImmutable();
        }
 
        internal ImmutableArray<Diagnostic> ApplyProgrammaticSuppressionsAndFilterDiagnostics(ImmutableArray<Diagnostic> reportedDiagnostics, Compilation compilation, CancellationToken cancellationToken)
        {
            if (reportedDiagnostics.IsEmpty)
            {
                return reportedDiagnostics;
            }
 
            var diagnostics = ApplyProgrammaticSuppressions(reportedDiagnostics, compilation, cancellationToken);
            if (compilation.Options.ReportSuppressedDiagnostics || diagnostics.All(d => !d.IsSuppressed))
            {
                return diagnostics;
            }
 
            return diagnostics.WhereAsArray(d => !d.IsSuppressed);
        }
 
        private bool IsInGeneratedCode(Location location, Compilation compilation, CancellationToken cancellationToken)
        {
            if (!location.IsInSource)
            {
                return false;
            }
 
            Debug.Assert(location.SourceTree != null);
 
            // Check if this is a generated code location.
            if (IsGeneratedOrHiddenCodeLocation(location.SourceTree, location.SourceSpan, cancellationToken))
            {
                return true;
            }
 
            // Check if the file has generated code definitions (i.e. symbols with GeneratedCodeAttribute).
            if (_lazyGeneratedCodeAttribute != null)
            {
                var generatedCodeSymbolsInTree = getOrComputeGeneratedCodeSymbolsInTree(location.SourceTree, compilation, cancellationToken);
                if (generatedCodeSymbolsInTree.Count > 0)
                {
                    var model = compilation.GetSemanticModel(location.SourceTree);
                    for (var node = location.SourceTree.GetRoot(cancellationToken).FindNode(location.SourceSpan, getInnermostNodeForTie: true);
                        node != null;
                        node = node.Parent)
                    {
                        var declaredSymbols = model.GetDeclaredSymbolsForNode(node, cancellationToken);
                        Debug.Assert(declaredSymbols != null);
 
                        foreach (var symbol in declaredSymbols)
                        {
                            if (generatedCodeSymbolsInTree.Contains(symbol))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
 
            return false;
 
            ImmutableHashSet<ISymbol> getOrComputeGeneratedCodeSymbolsInTree(SyntaxTree tree, Compilation compilation, CancellationToken cancellationToken)
            {
                Debug.Assert(GeneratedCodeSymbolsForTreeMap != null);
                Debug.Assert(_lazyGeneratedCodeAttribute != null);
 
                ImmutableHashSet<ISymbol>? generatedCodeSymbols;
                lock (GeneratedCodeSymbolsForTreeMap)
                {
                    if (GeneratedCodeSymbolsForTreeMap.TryGetValue(tree, out generatedCodeSymbols))
                    {
                        return generatedCodeSymbols;
                    }
                }
 
                generatedCodeSymbols = computeGeneratedCodeSymbolsInTree(tree, compilation, _lazyGeneratedCodeAttribute, cancellationToken);
 
                lock (GeneratedCodeSymbolsForTreeMap)
                {
                    ImmutableHashSet<ISymbol>? existingGeneratedCodeSymbols;
                    if (!GeneratedCodeSymbolsForTreeMap.TryGetValue(tree, out existingGeneratedCodeSymbols))
                    {
                        GeneratedCodeSymbolsForTreeMap.Add(tree, generatedCodeSymbols);
                    }
                    else
                    {
                        Debug.Assert(existingGeneratedCodeSymbols.SetEquals(generatedCodeSymbols));
                    }
                }
 
                return generatedCodeSymbols;
 
                static ImmutableHashSet<ISymbol> computeGeneratedCodeSymbolsInTree(SyntaxTree tree, Compilation compilation, INamedTypeSymbol generatedCodeAttribute, CancellationToken cancellationToken)
                {
                    var root = tree.GetRoot(cancellationToken);
 
                    // PERF: Bail out early if file doesn't have "GeneratedCode" text.
                    if (!containsGeneratedCodeToken(root))
                        return ImmutableHashSet<ISymbol>.Empty;
 
                    var model = compilation.GetSemanticModel(tree);
                    var span = root.FullSpan;
                    var declarationInfoBuilder = ArrayBuilder<DeclarationInfo>.GetInstance();
                    model.ComputeDeclarationsInSpan(span, getSymbol: true, builder: declarationInfoBuilder, cancellationToken: cancellationToken);
 
                    ImmutableHashSet<ISymbol>.Builder? generatedSymbolsBuilder = null;
                    foreach (var declarationInfo in declarationInfoBuilder)
                    {
                        var symbol = declarationInfo.DeclaredSymbol;
                        if (symbol != null &&
                            GeneratedCodeUtilities.IsGeneratedSymbolWithGeneratedCodeAttribute(symbol, generatedCodeAttribute))
                        {
                            generatedSymbolsBuilder ??= ImmutableHashSet.CreateBuilder<ISymbol>();
                            generatedSymbolsBuilder.Add(symbol);
                        }
                    }
 
                    declarationInfoBuilder.Free();
                    return generatedSymbolsBuilder != null ? generatedSymbolsBuilder.ToImmutable() : ImmutableHashSet<ISymbol>.Empty;
                }
 
                static bool containsGeneratedCodeToken(SyntaxNode root)
                {
                    return root.DescendantTokens().Any(static token => string.Equals(token.ValueText, "GeneratedCode", StringComparison.Ordinal) ||
                                                                       string.Equals(token.ValueText, nameof(GeneratedCodeAttribute), StringComparison.Ordinal));
                }
            }
        }
 
        private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree tree, SyntaxTreeOptionsProvider? options, CancellationToken cancellationToken)
        {
            if (!SuppressedAnalyzersForTreeMap.TryGetValue(tree, out var suppressedAnalyzers))
            {
                suppressedAnalyzers = SuppressedAnalyzersForTreeMap.GetOrAdd(tree, ComputeSuppressedAnalyzersForTree(tree, options, cancellationToken));
            }
 
            return suppressedAnalyzers.Contains(analyzer);
        }
 
        private ImmutableHashSet<DiagnosticAnalyzer> ComputeSuppressedAnalyzersForTree(SyntaxTree tree, SyntaxTreeOptionsProvider? options, CancellationToken cancellationToken)
        {
            if (options is null)
            {
                return ImmutableHashSet<DiagnosticAnalyzer>.Empty;
            }
 
            ImmutableHashSet<DiagnosticAnalyzer>.Builder? suppressedAnalyzersBuilder = null;
            foreach (var analyzer in UnsuppressedAnalyzers)
            {
                if (NonConfigurableAndCustomConfigurableAnalyzers.Contains(analyzer))
                {
                    // Analyzers reporting non-configurable or custom configurable diagnostics cannot be suppressed as user configuration is ignored for these analyzers.
                    continue;
                }
 
                if ((SymbolStartAnalyzers.Contains(analyzer) || CompilationEndAnalyzers.Contains(analyzer)) &&
                    !ShouldSkipAnalysisOnGeneratedCode(analyzer))
                {
                    // SymbolStart/End analyzers and CompilationStart/End analyzers that analyze generated code
                    // cannot have any of their callbacks suppressed as they need to analyze the entire compilation for correctness.
                    continue;
                }
 
                var descriptors = AnalyzerManager.GetSupportedDiagnosticDescriptors(analyzer, AnalyzerExecutor, cancellationToken);
                var hasUnsuppressedDiagnostic = false;
                foreach (var descriptor in descriptors)
                {
                    var configuredSeverity = descriptor.GetEffectiveSeverity(AnalyzerExecutor.Compilation.Options);
                    if (options.TryGetDiagnosticValue(tree, descriptor.Id, cancellationToken, out var severityFromOptions) ||
                        options.TryGetGlobalDiagnosticValue(descriptor.Id, cancellationToken, out severityFromOptions))
                    {
                        configuredSeverity = severityFromOptions;
                    }
 
                    // Disabled by default descriptor with default configured severity is equivalent to suppressed.
                    if (!descriptor.IsEnabledByDefault && configuredSeverity == ReportDiagnostic.Default)
                    {
                        configuredSeverity = ReportDiagnostic.Suppress;
                    }
 
                    if (configuredSeverity != ReportDiagnostic.Suppress)
                    {
                        // Analyzer reports a diagnostic that is not suppressed by the diagnostic options for this tree.
                        hasUnsuppressedDiagnostic = true;
                        break;
                    }
                }
 
                if (!hasUnsuppressedDiagnostic)
                {
                    suppressedAnalyzersBuilder ??= ImmutableHashSet.CreateBuilder<DiagnosticAnalyzer>();
                    suppressedAnalyzersBuilder.Add(analyzer);
                }
            }
 
            return suppressedAnalyzersBuilder != null ? suppressedAnalyzersBuilder.ToImmutable() : ImmutableHashSet<DiagnosticAnalyzer>.Empty;
        }
 
        public bool IsInitialized => _lazyInitializeTask != null;
 
        /// <summary>
        /// Return a task that completes when the driver is initialized.
        /// </summary>
        public Task WhenInitializedTask
        {
            get
            {
                Debug.Assert(_lazyInitializeTask != null);
                return _lazyInitializeTask;
            }
        }
 
        /// <summary>
        /// Return a task that completes when the driver is done producing diagnostics.
        /// </summary>
        public Task WhenCompletedTask
        {
            get
            {
                Debug.Assert(_lazyPrimaryTask != null);
                return _lazyPrimaryTask;
            }
        }
 
        internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => AnalyzerExecutor.AnalyzerExecutionTimes;
        internal TimeSpan ResetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer) => AnalyzerExecutor.ResetAnalyzerExecutionTime(analyzer);
 
        private static ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>)> MakeSymbolActionsByKind(in AnalyzerActions analyzerActions)
        {
            var builder = ArrayBuilder<(DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>)>.GetInstance();
            var actionsByAnalyzers = analyzerActions.SymbolActions.GroupBy(action => action.Analyzer);
            var actionsByKindBuilder = ArrayBuilder<ArrayBuilder<SymbolAnalyzerAction>>.GetInstance();
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                actionsByKindBuilder.Clear();
                foreach (var symbolAction in analyzerAndActions)
                {
                    var kinds = symbolAction.Kinds;
                    foreach (int kind in kinds.Distinct())
                    {
                        if (kind > MaxSymbolKind) continue; // protect against vicious analyzers
                        while (kind >= actionsByKindBuilder.Count)
                        {
                            actionsByKindBuilder.Add(ArrayBuilder<SymbolAnalyzerAction>.GetInstance());
                        }
 
                        actionsByKindBuilder[kind].Add(symbolAction);
                    }
                }
 
                var actionsByKind = actionsByKindBuilder.Select(a => a.ToImmutableAndFree()).ToImmutableArray();
                builder.Add((analyzerAndActions.Key, actionsByKind));
            }
 
            actionsByKindBuilder.Free();
            return builder.ToImmutableAndFree();
        }
 
        private static ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<TAnalyzerAction>)> MakeActionsByAnalyzer<TAnalyzerAction>(in ImmutableArray<TAnalyzerAction> analyzerActions)
            where TAnalyzerAction : AnalyzerAction
        {
            var builder = ArrayBuilder<(DiagnosticAnalyzer, ImmutableArray<TAnalyzerAction>)>.GetInstance();
            var actionsByAnalyzers = analyzerActions.GroupBy(action => action.Analyzer);
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                builder.Add((analyzerAndActions.Key, analyzerAndActions.ToImmutableArray()));
            }
 
            return builder.ToImmutableAndFree();
        }
 
        private static ImmutableHashSet<DiagnosticAnalyzer> MakeCompilationEndAnalyzers(ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>)> compilationEndActionsByAnalyzer)
        {
            var builder = ImmutableHashSet.CreateBuilder<DiagnosticAnalyzer>();
            foreach (var (analyzer, _) in compilationEndActionsByAnalyzer)
            {
                builder.Add(analyzer);
            }
 
            return builder.ToImmutable();
        }
 
        private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, bool prePopulatedEventQueue, CancellationToken cancellationToken)
        {
            try
            {
                CompilationCompletedEvent? completedEvent = null;
 
                if (analysisScope.ConcurrentAnalysis)
                {
                    // Kick off worker tasks to process all compilation events (except the compilation end event) in parallel.
                    // Compilation end event must be processed after all other events.
 
                    var workerCount = prePopulatedEventQueue ? Math.Min(CompilationEventQueue.Count, _workerCount) : _workerCount;
 
                    var workerTasks = new Task<CompilationCompletedEvent?>[workerCount];
                    for (int i = 0; i < workerCount; i++)
                    {
                        // Create separate worker tasks to process all compilation events - we do not want to process any events on the main thread.
                        workerTasks[i] = Task.Run(async () => await ProcessCompilationEventsCoreAsync(analysisScope, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false));
                    }
 
                    cancellationToken.ThrowIfCancellationRequested();
 
                    // Kick off tasks to execute syntax tree actions.
                    var syntaxTreeActionsTask = analysisScope.SyntaxTrees.Any()
                        ? Task.Run(() => ExecuteSyntaxTreeActions(analysisScope, cancellationToken), cancellationToken)
                        : Task.CompletedTask;
 
                    // Kick off tasks to execute additional file actions.
                    var additionalFileActionsTask = analysisScope.AdditionalFiles.Any()
                        ? Task.Run(() => ExecuteAdditionalFileActions(analysisScope, cancellationToken), cancellationToken)
                        : Task.CompletedTask;
 
                    // If necessary, wait for all worker threads to complete processing events.
                    if (workerTasks.Length > 0 || syntaxTreeActionsTask.Status != TaskStatus.RanToCompletion || additionalFileActionsTask.Status != TaskStatus.RanToCompletion)
                    {
                        await Task.WhenAll(workerTasks.Concat(syntaxTreeActionsTask).Concat(additionalFileActionsTask)).ConfigureAwait(false);
                    }
 
                    for (int i = 0; i < workerCount; i++)
                    {
                        if (workerTasks[i].Status == TaskStatus.RanToCompletion && workerTasks[i].Result != null)
                        {
                            completedEvent = workerTasks[i].Result;
                            break;
                        }
                    }
                }
                else
                {
                    completedEvent = await ProcessCompilationEventsCoreAsync(analysisScope, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false);
 
                    ExecuteSyntaxTreeActions(analysisScope, cancellationToken);
                    ExecuteAdditionalFileActions(analysisScope, cancellationToken);
                }
 
                // Finally process the compilation completed event, if any.
                if (completedEvent != null)
                {
                    await ProcessEventAsync(completedEvent, analysisScope, cancellationToken).ConfigureAwait(false);
                }
            }
            catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        private async Task<CompilationCompletedEvent?> ProcessCompilationEventsCoreAsync(AnalysisScope analysisScope, bool prePopulatedEventQueue, CancellationToken cancellationToken)
        {
            try
            {
                CompilationCompletedEvent? completedEvent = null;
 
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();
 
                    // NOTE: IsCompleted guarantees that Count will not increase
                    //       the reverse is not true, so we need to check IsCompleted first and then check the Count
                    if ((prePopulatedEventQueue || CompilationEventQueue.IsCompleted) &&
                        CompilationEventQueue.Count == 0)
                    {
                        break;
                    }
 
                    if (!CompilationEventQueue.TryDequeue(out var compilationEvent))
                    {
                        if (!prePopulatedEventQueue)
                        {
                            var optionalEvent = await CompilationEventQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
                            if (!optionalEvent.HasValue)
                            {
                                // When the queue is completed with a pending TryDequeueAsync return, the
                                // the Optional<T> will not have a value. This signals the queue has reached
                                // completion and no more items will be added to it.
                                Debug.Assert(CompilationEventQueue.IsCompleted, "TryDequeueAsync should provide a value unless the AsyncQueue<T> is completed.");
                                break;
                            }
 
                            compilationEvent = optionalEvent.Value;
                        }
                        else
                        {
                            return completedEvent;
                        }
                    }
 
                    // Don't process the compilation completed event as other worker threads might still be processing other compilation events.
                    // The caller will wait for all workers to complete and finally process this event.
                    if (compilationEvent is CompilationCompletedEvent compilationCompletedEvent)
                    {
                        completedEvent = compilationCompletedEvent;
                        continue;
                    }
 
                    await ProcessEventAsync(compilationEvent, analysisScope, cancellationToken).ConfigureAwait(false);
                }
 
                return completedEvent;
            }
            catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        private async Task ProcessEventAsync(CompilationEvent e, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            EventProcessedState eventProcessedState = await TryProcessEventCoreAsync(e, analysisScope, cancellationToken).ConfigureAwait(false);
 
            ImmutableArray<DiagnosticAnalyzer> processedAnalyzers;
            switch (eventProcessedState.Kind)
            {
                case EventProcessedStateKind.Processed:
                    processedAnalyzers = analysisScope.Analyzers;
                    break;
 
                case EventProcessedStateKind.PartiallyProcessed:
                    processedAnalyzers = eventProcessedState.SubsetProcessedAnalyzers;
                    break;
 
                default:
                    return;
            }
 
            await OnEventProcessedCoreAsync(e, processedAnalyzers, analysisScope, cancellationToken).ConfigureAwait(false);
        }
 
        private async Task OnEventProcessedCoreAsync(CompilationEvent compilationEvent, ImmutableArray<DiagnosticAnalyzer> processedAnalyzers, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            switch (compilationEvent)
            {
                case SymbolDeclaredCompilationEvent symbolDeclaredEvent:
                    if (AnalyzerActions.SymbolStartActionsCount > 0)
                    {
                        foreach (var analyzer in processedAnalyzers)
                        {
                            await onSymbolAndMembersProcessedAsync(symbolDeclaredEvent.Symbol, analyzer).ConfigureAwait(false);
                        }
                    }
 
                    break;
 
                case CompilationUnitCompletedEvent compilationUnitCompletedEvent when !compilationUnitCompletedEvent.FilterSpan.HasValue:
                    // Clear the semantic model cache only if we have completed analysis for the entire compilation unit,
                    // i.e. the event has a null filter span. Compilation unit completed event with a non-null filter span
                    // indicates a synthesized event for partial analysis of the tree and we avoid clearing the semantic model cache for that case.
                    SemanticModelProvider.ClearCache(compilationUnitCompletedEvent.CompilationUnit, compilationUnitCompletedEvent.Compilation);
                    break;
 
                case CompilationCompletedEvent compilationCompletedEvent:
                    SemanticModelProvider.ClearCache(compilationCompletedEvent.Compilation);
                    break;
            }
 
            return;
 
            async Task onSymbolAndMembersProcessedAsync(ISymbol symbol, DiagnosticAnalyzer analyzer)
            {
                if (AnalyzerActions.SymbolStartActionsCount == 0 || symbol.IsImplicitlyDeclared)
                {
                    return;
                }
 
                if (symbol is INamespaceOrTypeSymbol namespaceOrType)
                {
                    PerSymbolAnalyzerActionsCache.TryRemove((namespaceOrType, analyzer), out _);
                }
 
                await processContainerOnMemberCompletedAsync(symbol.ContainingNamespace, symbol, analyzer).ConfigureAwait(false);
 
                for (var type = symbol.ContainingType; type != null; type = type.ContainingType)
                    await processContainerOnMemberCompletedAsync(type, symbol, analyzer).ConfigureAwait(false);
            }
 
            async Task processContainerOnMemberCompletedAsync(INamespaceOrTypeSymbol containerSymbol, ISymbol processedMemberSymbol, DiagnosticAnalyzer analyzer)
            {
                if (containerSymbol != null &&
                    AnalyzerExecutor.TryExecuteSymbolEndActionsForContainer(containerSymbol, processedMemberSymbol,
                        analyzer, s_getTopmostNodeForAnalysis, IsGeneratedCodeSymbol(containerSymbol, cancellationToken),
                        analysisScope.OriginalFilterFile?.SourceTree, analysisScope.OriginalFilterSpan, cancellationToken, out var processedContainerEvent))
                {
                    await OnEventProcessedCoreAsync(processedContainerEvent, ImmutableArray.Create(analyzer), analysisScope, cancellationToken).ConfigureAwait(false);
                }
            }
        }
 
        [PerformanceSensitive(
            "https://developercommunity.visualstudio.com/content/problem/805524/ctrl-suggestions-are-very-slow-and-produce-gatheri.html",
            OftenCompletesSynchronously = true)]
        private async ValueTask<EventProcessedState> TryProcessEventCoreAsync(CompilationEvent compilationEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            switch (compilationEvent)
            {
                case SymbolDeclaredCompilationEvent symbolEvent:
                    return await TryProcessSymbolDeclaredAsync(symbolEvent, analysisScope, cancellationToken).ConfigureAwait(false);
 
                case CompilationUnitCompletedEvent completedEvent:
                    ProcessCompilationUnitCompleted(completedEvent, analysisScope, cancellationToken);
                    return EventProcessedState.Processed;
 
                case CompilationCompletedEvent endEvent:
                    ProcessCompilationCompleted(endEvent, analysisScope, cancellationToken);
                    return EventProcessedState.Processed;
 
                case CompilationStartedEvent startedEvent:
                    ProcessCompilationStarted(startedEvent, analysisScope, cancellationToken);
                    return EventProcessedState.Processed;
 
                default:
                    throw new InvalidOperationException("Unexpected compilation event of type " + compilationEvent.GetType().Name);
            }
        }
 
        /// <summary>
        /// Tries to execute symbol action, symbol start/end actions and declaration actions for the given symbol.
        /// </summary>
        /// <returns>
        /// <see cref="EventProcessedState"/> indicating the current state of processing of the given compilation event.
        /// </returns>
        [PerformanceSensitive(
            "https://developercommunity.visualstudio.com/content/problem/805524/ctrl-suggestions-are-very-slow-and-produce-gatheri.html",
            OftenCompletesSynchronously = true)]
        private async ValueTask<EventProcessedState> TryProcessSymbolDeclaredAsync(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            var symbol = symbolEvent.Symbol;
            var isGeneratedCodeSymbol = IsGeneratedCodeSymbol(symbol, cancellationToken);
 
            var skipSymbolAnalysis = AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent);
            var skipDeclarationAnalysis = AnalysisScope.ShouldSkipDeclarationAnalysis(symbol);
            var hasPerSymbolActions = AnalyzerActions.SymbolStartActionsCount > 0 && (!skipSymbolAnalysis || !skipDeclarationAnalysis);
 
            var perSymbolActions = hasPerSymbolActions ?
                await GetPerSymbolAnalyzerActionsAsync(symbol, analysisScope, cancellationToken).ConfigureAwait(false) :
                EmptyGroupedActions;
 
            if (!skipSymbolAnalysis)
            {
                ExecuteSymbolActions(symbolEvent, analysisScope, isGeneratedCodeSymbol, cancellationToken);
            }
 
            if (!skipDeclarationAnalysis)
            {
                ExecuteDeclaringReferenceActions(symbolEvent, analysisScope, isGeneratedCodeSymbol, perSymbolActions, cancellationToken);
            }
 
            if (hasPerSymbolActions &&
                !TryExecuteSymbolEndActions(perSymbolActions.AnalyzerActions, symbolEvent, analysisScope, isGeneratedCodeSymbol, cancellationToken, out var subsetProcessedAnalyzers))
            {
                Debug.Assert(!subsetProcessedAnalyzers.IsDefault);
                return subsetProcessedAnalyzers.IsEmpty ? EventProcessedState.NotProcessed : EventProcessedState.CreatePartiallyProcessed(subsetProcessedAnalyzers);
            }
 
            return EventProcessedState.Processed;
        }
 
        private void ExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, bool isGeneratedCodeSymbol, CancellationToken cancellationToken)
        {
            var symbol = symbolEvent.Symbol;
            if (!analysisScope.ShouldAnalyze(symbolEvent, s_getTopmostNodeForAnalysis, cancellationToken))
            {
                return;
            }
 
            foreach (var (analyzer, actionsByKind) in _lazySymbolActionsByKind)
            {
                if (!analysisScope.Contains(analyzer))
                {
                    continue;
                }
 
                // Invoke symbol analyzers only for source symbols.
                if ((int)symbol.Kind < actionsByKind.Length)
                {
                    AnalyzerExecutor.ExecuteSymbolActions(actionsByKind[(int)symbol.Kind], analyzer, symbolEvent, s_getTopmostNodeForAnalysis, isGeneratedCodeSymbol, analysisScope.FilterFileOpt?.SourceTree, analysisScope.FilterSpanOpt, cancellationToken);
                }
            }
        }
 
        private bool TryExecuteSymbolEndActions(
            in AnalyzerActions perSymbolActions,
            SymbolDeclaredCompilationEvent symbolEvent,
            AnalysisScope analysisScope,
            bool isGeneratedCodeSymbol,
            CancellationToken cancellationToken,
            out ImmutableArray<DiagnosticAnalyzer> subsetProcessedAnalyzers)
        {
            Debug.Assert(AnalyzerActions.SymbolStartActionsCount > 0);
 
            var symbol = symbolEvent.Symbol;
            var symbolEndActions = perSymbolActions.SymbolEndActions;
            if (symbolEndActions.IsEmpty || !analysisScope.ShouldAnalyze(symbolEvent, s_getTopmostNodeForAnalysis, cancellationToken))
            {
                subsetProcessedAnalyzers = ImmutableArray<DiagnosticAnalyzer>.Empty;
                return true;
            }
 
            var success = true;
            var completedAnalyzers = ArrayBuilder<DiagnosticAnalyzer>.GetInstance();
            var processedAnalyzers = PooledHashSet<DiagnosticAnalyzer>.GetInstance();
            try
            {
                foreach (var groupedActions in symbolEndActions.GroupBy(a => a.Analyzer))
                {
                    var analyzer = groupedActions.Key;
                    if (!analysisScope.Contains(analyzer))
                    {
                        continue;
                    }
 
                    processedAnalyzers.Add(analyzer);
 
                    var symbolEndActionsForAnalyzer = groupedActions.ToImmutableArrayOrEmpty();
                    if (!symbolEndActionsForAnalyzer.IsEmpty &&
                        !AnalyzerExecutor.TryExecuteSymbolEndActions(symbolEndActionsForAnalyzer, analyzer, symbolEvent, s_getTopmostNodeForAnalysis, isGeneratedCodeSymbol, analysisScope.OriginalFilterFile?.SourceTree, analysisScope.OriginalFilterSpan, cancellationToken))
                    {
                        success = false;
                        continue;
                    }
 
                    AnalyzerManager.MarkSymbolEndAnalysisComplete(symbol, analyzer);
                    completedAnalyzers.Add(analyzer);
                }
 
                if (processedAnalyzers.Count < analysisScope.Analyzers.Length)
                {
                    foreach (var analyzer in analysisScope.Analyzers)
                    {
                        if (!processedAnalyzers.Contains(analyzer))
                        {
                            AnalyzerManager.MarkSymbolEndAnalysisComplete(symbol, analyzer);
                            completedAnalyzers.Add(analyzer);
                        }
                    }
                }
 
                if (!success)
                {
                    Debug.Assert(completedAnalyzers.Count < analysisScope.Analyzers.Length);
                    subsetProcessedAnalyzers = completedAnalyzers.ToImmutable();
                    return false;
                }
                else
                {
                    subsetProcessedAnalyzers = ImmutableArray<DiagnosticAnalyzer>.Empty;
                    return true;
                }
            }
            finally
            {
                processedAnalyzers.Free();
                completedAnalyzers.Free();
            }
        }
 
        private static SyntaxNode GetTopmostNodeForAnalysis(ISymbol symbol, SyntaxReference syntaxReference, Compilation compilation, CancellationToken cancellationToken)
        {
            var model = compilation.GetSemanticModel(syntaxReference.SyntaxTree);
            return model.GetTopmostNodeForDiagnosticAnalysis(symbol, syntaxReference.GetSyntax(cancellationToken));
        }
 
        protected abstract void ExecuteDeclaringReferenceActions(
            SymbolDeclaredCompilationEvent symbolEvent,
            AnalysisScope analysisScope,
            bool isGeneratedCodeSymbol,
            IGroupedAnalyzerActions additionalPerSymbolActions,
            CancellationToken cancellationToken);
 
        private void ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent completedEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            // When the compiler is finished with a compilation unit, we can run user diagnostics which
            // might want to ask the compiler for all the diagnostics in the source file, for example
            // to get information about unnecessary usings.
 
            var semanticModel = GetOrCreateSemanticModel(completedEvent.CompilationUnit, completedEvent.Compilation);
            if (!analysisScope.ShouldAnalyze(semanticModel.SyntaxTree))
            {
                return;
            }
 
            var isGeneratedCode = IsGeneratedCode(semanticModel.SyntaxTree, cancellationToken);
            if (isGeneratedCode && DoNotAnalyzeGeneratedCode)
            {
                return;
            }
 
            foreach (var (analyzer, semanticModelActions) in _lazySemanticModelActions)
            {
                if (!analysisScope.Contains(analyzer))
                {
                    continue;
                }
 
                // Execute actions for a given analyzer sequentially.
                AnalyzerExecutor.ExecuteSemanticModelActions(semanticModelActions, analyzer, semanticModel, analysisScope.FilterSpanOpt, isGeneratedCode, cancellationToken);
            }
        }
 
        private void ProcessCompilationStarted(CompilationStartedEvent startedEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            ExecuteCompilationActions(_lazyCompilationActions, startedEvent, analysisScope, cancellationToken);
        }
 
        private void ProcessCompilationCompleted(CompilationCompletedEvent endEvent, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            ExecuteCompilationActions(_lazyCompilationEndActions, endEvent, analysisScope, cancellationToken);
        }
 
        private void ExecuteCompilationActions(
            ImmutableArray<(DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>)> compilationActionsMap,
            CompilationEvent compilationEvent,
            AnalysisScope analysisScope,
            CancellationToken cancellationToken)
        {
            Debug.Assert(compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent);
 
            foreach (var (analyzer, compilationActions) in compilationActionsMap)
            {
                if (!analysisScope.Contains(analyzer))
                {
                    continue;
                }
 
                AnalyzerExecutor.ExecuteCompilationActions(compilationActions, analyzer, compilationEvent, cancellationToken);
            }
        }
 
        internal static Action<Diagnostic, CancellationToken> GetDiagnosticSink(Action<Diagnostic> addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet<string>? suppressedDiagnosticIds)
        {
            return (diagnostic, cancellationToken) =>
            {
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken);
                if (filteredDiagnostic != null)
                {
                    addDiagnosticCore(filteredDiagnostic);
                }
            };
        }
 
        internal static Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken> GetDiagnosticSink(Action<Diagnostic, DiagnosticAnalyzer, bool> addLocalDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet<string>? suppressedDiagnosticIds)
        {
            return (diagnostic, analyzer, isSyntaxDiagnostic, cancellationToken) =>
            {
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken);
                if (filteredDiagnostic != null)
                {
                    addLocalDiagnosticCore(filteredDiagnostic, analyzer, isSyntaxDiagnostic);
                }
            };
        }
 
        internal static Action<Diagnostic, DiagnosticAnalyzer, CancellationToken> GetDiagnosticSink(Action<Diagnostic, DiagnosticAnalyzer> addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet<string>? suppressedDiagnosticIds)
        {
            return (diagnostic, analyzer, cancellationToken) =>
            {
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken);
                if (filteredDiagnostic != null)
                {
                    addDiagnosticCore(filteredDiagnostic, analyzer);
                }
            };
        }
 
        private static Diagnostic? GetFilteredDiagnostic(Diagnostic diagnostic, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet<string>? suppressedDiagnosticIds, CancellationToken cancellationToken)
        {
            var filteredDiagnostic = compilation.Options.FilterDiagnostic(diagnostic, cancellationToken);
            filteredDiagnostic = applyFurtherFiltering(filteredDiagnostic);
 
            // Track diagnostics suppressed through compilation options or syntax tree options.
            if (filteredDiagnostic == null)
                suppressedDiagnosticIds?.Add(diagnostic.Id);
 
            return filteredDiagnostic;
 
            Diagnostic? applyFurtherFiltering(Diagnostic? diagnostic)
            {
                // Apply bulk configuration from analyzer options for analyzer diagnostics, if applicable.
                if (diagnostic?.Location.SourceTree is { } tree &&
                    analyzerOptions.TryGetSeverityFromBulkConfiguration(tree, compilation, diagnostic.Descriptor, cancellationToken, out ReportDiagnostic severity))
                {
                    diagnostic = diagnostic.WithReportDiagnostic(severity);
                }
 
                if (diagnostic != null &&
                    severityFilter.Contains(DiagnosticDescriptor.MapSeverityToReport(diagnostic.Severity)))
                {
                    return null;
                }
 
                return diagnostic;
            }
        }
 
        private static async Task<(AnalyzerActions actions, ImmutableHashSet<DiagnosticAnalyzer> unsuppressedAnalyzers)> GetAnalyzerActionsAsync(
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor,
            AnalysisScope analysisScope,
            SeverityFilter severityFilter,
            CancellationToken cancellationToken)
        {
            var allAnalyzerActions = AnalyzerActions.Empty;
            var unsuppressedAnalyzersBuilder = PooledHashSet<DiagnosticAnalyzer>.GetInstance();
            foreach (var analyzer in analyzers)
            {
                if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor, analysisScope, severityFilter, cancellationToken))
                {
                    unsuppressedAnalyzersBuilder.Add(analyzer);
 
                    var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor, cancellationToken).ConfigureAwait(false);
                    allAnalyzerActions = allAnalyzerActions.Append(in analyzerActions);
                }
            }
 
            var unsuppressedAnalyzers = unsuppressedAnalyzersBuilder.ToImmutableHashSet();
            unsuppressedAnalyzersBuilder.Free();
            return (allAnalyzerActions, unsuppressedAnalyzers);
        }
 
        public bool HasSymbolStartedActions(AnalysisScope analysisScope)
        {
            if (this.AnalyzerActions.SymbolStartActionsCount == 0)
            {
                return false;
            }
 
            // Perform simple checks for when we are executing all analyzers (batch compilation mode) OR
            // executing just a single analyzer (IDE open file analysis).
            if (analysisScope.Analyzers.Length == this.Analyzers.Length)
            {
                // We are executing all analyzers, so at least one analyzer in analysis scope must have a symbol start action.
                return true;
            }
            else if (analysisScope.Analyzers.Length == 1)
            {
                // We are executing a single analyzer.
                var analyzer = analysisScope.Analyzers[0];
                foreach (var action in this.AnalyzerActions.SymbolStartActions)
                {
                    if (action.Analyzer == analyzer)
                    {
                        return true;
                    }
                }
 
                return false;
            }
 
            // Slow check when we are executing more than one analyzer, but it is still a strict subset of all analyzers.
            var symbolStartAnalyzers = PooledHashSet<DiagnosticAnalyzer>.GetInstance();
            try
            {
                foreach (var action in this.AnalyzerActions.SymbolStartActions)
                {
                    symbolStartAnalyzers.Add(action.Analyzer);
                }
 
                foreach (var analyzer in analysisScope.Analyzers)
                {
                    if (symbolStartAnalyzers.Contains(analyzer))
                    {
                        return true;
                    }
                }
 
                return false;
            }
            finally
            {
                symbolStartAnalyzers.Free();
            }
        }
 
        [PerformanceSensitive(
            "https://developercommunity.visualstudio.com/content/problem/805524/ctrl-suggestions-are-very-slow-and-produce-gatheri.html",
            OftenCompletesSynchronously = true)]
        private async ValueTask<IGroupedAnalyzerActions> GetPerSymbolAnalyzerActionsAsync(
            ISymbol symbol,
            AnalysisScope analysisScope,
            CancellationToken cancellationToken)
        {
            if (AnalyzerActions.SymbolStartActionsCount == 0 || symbol.IsImplicitlyDeclared)
            {
                return EmptyGroupedActions;
            }
 
            var allActions = EmptyGroupedActions;
            foreach (var analyzer in analysisScope.Analyzers)
            {
                if (!SymbolStartAnalyzers.Contains(analyzer))
                {
                    continue;
                }
 
                var analyzerActions = await GetPerSymbolAnalyzerActionsAsync(symbol, analyzer, analysisScope.OriginalFilterFile?.SourceTree, analysisScope.OriginalFilterSpan, cancellationToken).ConfigureAwait(false);
                if (!analyzerActions.IsEmpty)
                {
                    allActions = allActions.Append(analyzerActions);
                }
            }
 
            return allActions;
        }
 
        [PerformanceSensitive(
            "https://developercommunity.visualstudio.com/content/problem/805524/ctrl-suggestions-are-very-slow-and-produce-gatheri.html",
            OftenCompletesSynchronously = true)]
        private async ValueTask<IGroupedAnalyzerActions> GetPerSymbolAnalyzerActionsAsync(
            ISymbol symbol,
            DiagnosticAnalyzer analyzer,
            SyntaxTree? filterTree,
            TextSpan? filterSpan,
            CancellationToken cancellationToken)
        {
            Debug.Assert(AnalyzerActions.SymbolStartActionsCount > 0);
            Debug.Assert(SymbolStartAnalyzers.Contains(analyzer));
 
            if (symbol.IsImplicitlyDeclared)
            {
                return EmptyGroupedActions;
            }
 
            // PERF: For containing symbols, we want to cache the computed actions.
            // For member symbols, we do not want to cache as we will not reach this path again.
            if (symbol is not INamespaceOrTypeSymbol namespaceOrType)
            {
                return await getAllActionsAsync(this, symbol, analyzer, filterTree, filterSpan, cancellationToken).ConfigureAwait(false);
            }
 
            if (PerSymbolAnalyzerActionsCache.TryGetValue((namespaceOrType, analyzer), out var actions))
            {
                return actions;
            }
 
            var allActions = await getAllActionsAsync(this, symbol, analyzer, filterTree, filterSpan, cancellationToken).ConfigureAwait(false);
            return PerSymbolAnalyzerActionsCache.GetOrAdd((namespaceOrType, analyzer), allActions);
 
            async ValueTask<IGroupedAnalyzerActions> getAllActionsAsync(AnalyzerDriver driver, ISymbol symbol, DiagnosticAnalyzer analyzer, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
            {
                // Compute additional inherited actions for this symbol by running the containing symbol's start actions.
                var inheritedActions = await getInheritedActionsAsync(driver, symbol, analyzer, filterTree, filterSpan, cancellationToken).ConfigureAwait(false);
 
                // Execute the symbol start actions for this symbol to compute additional actions for its members.
                AnalyzerActions myActions = await getSymbolActionsCoreAsync(driver, symbol, analyzer, filterTree, filterSpan, cancellationToken).ConfigureAwait(false);
                if (myActions.IsEmpty)
                {
                    return inheritedActions;
                }
 
                var allActions = inheritedActions.AnalyzerActions.Append(in myActions);
                return CreateGroupedActions(analyzer, allActions);
            }
 
            async ValueTask<IGroupedAnalyzerActions> getInheritedActionsAsync(AnalyzerDriver driver, ISymbol symbol, DiagnosticAnalyzer analyzer, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
            {
                if (symbol.ContainingSymbol != null)
                {
                    // Get container symbol's per-symbol actions, which also forces its start actions to execute.
                    var containerActions = await driver.GetPerSymbolAnalyzerActionsAsync(symbol.ContainingSymbol, analyzer, filterTree, filterSpan, cancellationToken).ConfigureAwait(false);
                    if (!containerActions.IsEmpty)
                    {
                        // Don't inherit actions for nested type and namespace from its containing type and namespace respectively.
                        // However, note that we bail out **after** computing container's per-symbol actions above.
                        // This is done to ensure that we have executed symbol started actions for the container before our start actions are executed.
                        if (symbol.ContainingSymbol.Kind != symbol.Kind)
                        {
                            // Don't inherit the symbol start and symbol end actions.
                            var containerAnalyzerActions = containerActions.AnalyzerActions;
                            var actions = AnalyzerActions.Empty.Append(in containerAnalyzerActions, appendSymbolStartAndSymbolEndActions: false);
                            return CreateGroupedActions(analyzer, actions);
                        }
                    }
                }
 
                return EmptyGroupedActions;
            }
 
            static async ValueTask<AnalyzerActions> getSymbolActionsCoreAsync(AnalyzerDriver driver, ISymbol symbol, DiagnosticAnalyzer analyzer, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
            {
                if (!driver.UnsuppressedAnalyzers.Contains(analyzer))
                {
                    return AnalyzerActions.Empty;
                }
 
                var isGeneratedCodeSymbol = driver.IsGeneratedCodeSymbol(symbol, cancellationToken);
                if (isGeneratedCodeSymbol && driver.ShouldSkipAnalysisOnGeneratedCode(analyzer))
                {
                    return AnalyzerActions.Empty;
                }
 
                return await driver.AnalyzerManager.GetPerSymbolAnalyzerActionsAsync(symbol, isGeneratedCodeSymbol, filterTree, filterSpan, analyzer, driver.AnalyzerExecutor, cancellationToken).ConfigureAwait(false);
            }
        }
 
        private static async Task<ImmutableSegmentedDictionary<DiagnosticAnalyzer, SemaphoreSlim>> CreateAnalyzerGateMapAsync(
            ImmutableHashSet<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor,
            AnalysisScope analysisScope,
            SeverityFilter severityFilter,
            CancellationToken cancellationToken)
        {
            var builder = ImmutableSegmentedDictionary.CreateBuilder<DiagnosticAnalyzer, SemaphoreSlim>();
            foreach (var analyzer in analyzers)
            {
                Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor, analysisScope, severityFilter, cancellationToken));
 
                var isConcurrent = await analyzerManager.IsConcurrentAnalyzerAsync(analyzer, analyzerExecutor, cancellationToken).ConfigureAwait(false);
                if (!isConcurrent)
                {
                    // Non-concurrent analyzers need their action callbacks from the analyzer driver to be guarded by a gate.
                    var gate = new SemaphoreSlim(initialCount: 1);
                    builder.Add(analyzer, gate);
                }
            }
 
            return builder.ToImmutable();
        }
 
        private static async Task<ImmutableSegmentedDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>> CreateGeneratedCodeAnalysisFlagsMapAsync(
            ImmutableHashSet<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor,
            AnalysisScope analysisScope,
            SeverityFilter severityFilter,
            CancellationToken cancellationToken)
        {
            var builder = ImmutableSegmentedDictionary.CreateBuilder<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>();
            foreach (var analyzer in analyzers)
            {
                Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor, analysisScope, severityFilter, cancellationToken));
 
                var generatedCodeAnalysisFlags = await analyzerManager.GetGeneratedCodeAnalysisFlagsAsync(analyzer, analyzerExecutor, cancellationToken).ConfigureAwait(false);
                builder.Add(analyzer, generatedCodeAnalysisFlags);
            }
 
            return builder.ToImmutable();
        }
 
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/pull/23637",
            AllowLocks = false)]
        private bool IsGeneratedCodeSymbol(ISymbol symbol, CancellationToken cancellationToken)
        {
            return IsGeneratedCodeSymbolMap.TryGetValue(symbol, out bool isGeneratedCodeSymbol) ?
                isGeneratedCodeSymbol :
                IsGeneratedCodeSymbolMap.GetOrAdd(symbol, computeIsGeneratedCodeSymbol());
 
            bool computeIsGeneratedCodeSymbol()
            {
                if (_lazyGeneratedCodeAttribute != null && GeneratedCodeUtilities.IsGeneratedSymbolWithGeneratedCodeAttribute(symbol, _lazyGeneratedCodeAttribute))
                {
                    return true;
                }
 
                foreach (var declaringRef in symbol.DeclaringSyntaxReferences)
                {
                    if (!IsGeneratedOrHiddenCodeLocation(declaringRef.SyntaxTree, declaringRef.Span, cancellationToken))
                    {
                        return false;
                    }
                }
 
                return true;
            }
        }
 
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/pull/23637",
            AllowLocks = false)]
        protected bool IsGeneratedCode(SyntaxTree tree, CancellationToken cancellationToken)
        {
            if (!GeneratedCodeFilesMap.TryGetValue(tree, out var isGenerated))
            {
                isGenerated = computeIsGeneratedCode();
                GeneratedCodeFilesMap.TryAdd(tree, isGenerated);
            }
 
            return isGenerated;
 
            bool computeIsGeneratedCode()
            {
                // Check for explicit user configuration for generated code from options.
                //     generated_code = true | false
                // If there is no explicit user configuration, fallback to our generated code heuristic.
                var options = AnalyzerExecutor.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree);
                return GeneratedCodeUtilities.GetGeneratedCodeKindFromOptions(options).ToNullable() ??
                    _isGeneratedCode(tree, cancellationToken);
            }
        }
 
        protected bool DoNotAnalyzeGeneratedCode
        {
            get
            {
                Debug.Assert(_lazyDoNotAnalyzeGeneratedCode.HasValue);
                return _lazyDoNotAnalyzeGeneratedCode.Value;
            }
        }
 
        // Location is in generated code if either the containing tree is a generated code file OR if it is a hidden source location.
        protected bool IsGeneratedOrHiddenCodeLocation(SyntaxTree syntaxTree, TextSpan span, CancellationToken cancellationToken)
            => IsGeneratedCode(syntaxTree, cancellationToken) || IsHiddenSourceLocation(syntaxTree, span);
 
        protected bool IsHiddenSourceLocation(SyntaxTree syntaxTree, TextSpan span)
            => HasHiddenRegions(syntaxTree) &&
               syntaxTree.IsHiddenPosition(span.Start);
 
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/pull/23637",
            AllowLocks = false)]
        private bool HasHiddenRegions(SyntaxTree tree)
        {
            if (_lazyTreesWithHiddenRegionsMap == null)
            {
                return false;
            }
 
            bool hasHiddenRegions;
            if (!_lazyTreesWithHiddenRegionsMap.TryGetValue(tree, out hasHiddenRegions))
            {
                hasHiddenRegions = tree.HasHiddenRegions();
                _lazyTreesWithHiddenRegionsMap.TryAdd(tree, hasHiddenRegions);
            }
 
            return hasHiddenRegions;
        }
 
        internal async Task<AnalyzerActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CompilationOptions compilationOptions, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            if (IsDiagnosticAnalyzerSuppressed(analyzer, compilationOptions, AnalyzerManager, AnalyzerExecutor, analysisScope, _severityFilter, cancellationToken))
            {
                return AnalyzerActionCounts.Empty;
            }
 
            var analyzerActions = await AnalyzerManager.GetAnalyzerActionsAsync(analyzer, AnalyzerExecutor, cancellationToken).ConfigureAwait(false);
            if (analyzerActions.IsEmpty)
            {
                return AnalyzerActionCounts.Empty;
            }
 
            return new AnalyzerActionCounts(in analyzerActions);
        }
 
        /// <summary>
        /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options.
        /// </summary>
        internal static bool IsDiagnosticAnalyzerSuppressed(
            DiagnosticAnalyzer analyzer,
            CompilationOptions options,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor,
            AnalysisScope analysisScope,
            SeverityFilter severityFilter,
            CancellationToken cancellationToken)
        {
            return analyzerManager.IsDiagnosticAnalyzerSuppressed(analyzer, options, s_IsCompilerAnalyzerFunc, analyzerExecutor, analysisScope, severityFilter, cancellationToken);
        }
 
        internal static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer) => analyzer is CompilerDiagnosticAnalyzer;
 
        public void Dispose()
        {
            _lazyCompilationEventQueue?.TryComplete();
            _lazyDiagnosticQueue?.TryComplete();
            _lazyQueueRegistration?.Dispose();
        }
    }
 
    /// <summary>
    /// Driver to execute diagnostic analyzers for a given compilation.
    /// It uses a <see cref="AsyncQueue{TElement}"/> of <see cref="CompilationEvent"/>s to drive its analysis.
    /// </summary>
    internal partial class AnalyzerDriver<TLanguageKindEnum> : AnalyzerDriver where TLanguageKindEnum : struct
    {
        private readonly Func<SyntaxNode, TLanguageKindEnum> _getKind;
        private GroupedAnalyzerActions? _lazyCoreActions;
 
        /// <summary>
        /// Create an analyzer driver.
        /// </summary>
        /// <param name="analyzers">The set of analyzers to include in the analysis</param>
        /// <param name="getKind">A delegate that returns the language-specific kind for a given syntax node</param>
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
        /// <param name="severityFilter">Filtered diagnostic severities in the compilation, i.e. diagnostics with effective severity from this set should not be reported.</param>
        /// <param name="isComment">Delegate to identify if the given trivia is a comment.</param>
        internal AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<SyntaxNode, TLanguageKindEnum> getKind, AnalyzerManager analyzerManager, SeverityFilter severityFilter, Func<SyntaxTrivia, bool> isComment)
            : base(analyzers, analyzerManager, severityFilter, isComment)
        {
            _getKind = getKind;
        }
 
        private GroupedAnalyzerActions GetOrCreateCoreActions()
        {
            if (_lazyCoreActions == null)
            {
                Interlocked.CompareExchange(ref _lazyCoreActions, createCoreActions(), null);
            }
 
            return _lazyCoreActions;
 
            GroupedAnalyzerActions createCoreActions()
            {
                if (AnalyzerActions.IsEmpty)
                {
                    return GroupedAnalyzerActions.Empty;
                }
 
                // Keep analyzers in original order.
                var analyzers = Analyzers.WhereAsArray(UnsuppressedAnalyzers.Contains);
                return GroupedAnalyzerActions.Create(analyzers, AnalyzerActions);
            }
        }
 
        private static void ComputeShouldExecuteActions(
            in AnalyzerActions coreActions,
            in AnalyzerActions additionalActions,
            ISymbol symbol,
            out bool executeSyntaxNodeActions,
            out bool executeCodeBlockActions,
            out bool executeOperationActions,
            out bool executeOperationBlockActions)
        {
            executeSyntaxNodeActions = false;
            executeCodeBlockActions = false;
            executeOperationActions = false;
            executeOperationBlockActions = false;
 
            var canHaveExecutableCodeBlock = AnalyzerExecutor.CanHaveExecutableCodeBlock(symbol);
            computeShouldExecuteActions(coreActions, canHaveExecutableCodeBlock, ref executeSyntaxNodeActions, ref executeCodeBlockActions, ref executeOperationActions, ref executeOperationBlockActions);
            computeShouldExecuteActions(additionalActions, canHaveExecutableCodeBlock, ref executeSyntaxNodeActions, ref executeCodeBlockActions, ref executeOperationActions, ref executeOperationBlockActions);
            return;
 
            static void computeShouldExecuteActions(
                AnalyzerActions analyzerActions,
                bool canHaveExecutableCodeBlock,
                ref bool executeSyntaxNodeActions,
                ref bool executeCodeBlockActions,
                ref bool executeOperationActions,
                ref bool executeOperationBlockActions)
            {
                if (analyzerActions.IsEmpty)
                {
                    return;
                }
 
                executeSyntaxNodeActions |= analyzerActions.SyntaxNodeActionsCount > 0;
                executeOperationActions |= analyzerActions.OperationActionsCount > 0;
 
                if (canHaveExecutableCodeBlock)
                {
                    executeCodeBlockActions |= analyzerActions.CodeBlockStartActionsCount > 0 || analyzerActions.CodeBlockActionsCount > 0;
                    executeOperationBlockActions |= analyzerActions.OperationBlockStartActionsCount > 0 || analyzerActions.OperationBlockActionsCount > 0;
                }
            }
        }
 
        protected override IGroupedAnalyzerActions EmptyGroupedActions => GroupedAnalyzerActions.Empty;
        protected override IGroupedAnalyzerActions CreateGroupedActions(DiagnosticAnalyzer analyzer, in AnalyzerActions analyzerActions)
            => GroupedAnalyzerActions.Create(analyzer, analyzerActions);
 
        /// <summary>
        /// Execute syntax node, code block and operation actions for all declarations for the given symbol.
        /// </summary>
        protected override void ExecuteDeclaringReferenceActions(
            SymbolDeclaredCompilationEvent symbolEvent,
            AnalysisScope analysisScope,
            bool isGeneratedCodeSymbol,
            IGroupedAnalyzerActions additionalPerSymbolActions,
            CancellationToken cancellationToken)
        {
            var symbol = symbolEvent.Symbol;
 
            ComputeShouldExecuteActions(
                AnalyzerActions, additionalPerSymbolActions.AnalyzerActions, symbol,
                executeSyntaxNodeActions: out var executeSyntaxNodeActions,
                executeCodeBlockActions: out var executeCodeBlockActions,
                executeOperationActions: out var executeOperationActions,
                executeOperationBlockActions: out var executeOperationBlockActions);
 
            if (executeSyntaxNodeActions || executeOperationActions || executeCodeBlockActions || executeOperationBlockActions)
            {
                var declaringReferences = symbolEvent.DeclaringSyntaxReferences;
                var coreActions = GetOrCreateCoreActions();
                foreach (var decl in declaringReferences)
                {
                    cancellationToken.ThrowIfCancellationRequested();
 
                    if (analysisScope.FilterFileOpt != null && analysisScope.FilterFileOpt?.SourceTree != decl.SyntaxTree)
                    {
                        continue;
                    }
 
                    var isInGeneratedCode = isGeneratedCodeSymbol || IsGeneratedOrHiddenCodeLocation(decl.SyntaxTree, decl.Span, cancellationToken);
                    if (isInGeneratedCode && DoNotAnalyzeGeneratedCode)
                    {
                        continue;
                    }
 
                    ExecuteDeclaringReferenceActions(decl, symbolEvent, analysisScope, coreActions, (GroupedAnalyzerActions)additionalPerSymbolActions,
                        executeSyntaxNodeActions, executeOperationActions, executeCodeBlockActions, executeOperationBlockActions, isInGeneratedCode, cancellationToken);
                }
            }
        }
 
        private static DeclarationAnalysisData ComputeDeclarationAnalysisData(
            ISymbol symbol,
            SyntaxReference declaration,
            SemanticModel semanticModel,
            AnalysisScope analysisScope,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            var builder = ArrayBuilder<DeclarationInfo>.GetInstance();
            SyntaxNode declaringReferenceSyntax = declaration.GetSyntax(cancellationToken);
            SyntaxNode topmostNodeForAnalysis = semanticModel.GetTopmostNodeForDiagnosticAnalysis(symbol, declaringReferenceSyntax);
            ComputeDeclarationsInNode(semanticModel, symbol, declaringReferenceSyntax, topmostNodeForAnalysis, builder, cancellationToken);
            ImmutableArray<DeclarationInfo> declarationInfos = builder.ToImmutableAndFree();
 
            bool isPartialDeclAnalysis = analysisScope.FilterSpanOpt.HasValue && !analysisScope.ContainsSpan(topmostNodeForAnalysis.FullSpan);
            var data = new DeclarationAnalysisData(declaringReferenceSyntax, topmostNodeForAnalysis, declarationInfos, isPartialDeclAnalysis);
            AddSyntaxNodesToAnalyze(topmostNodeForAnalysis, symbol, declarationInfos, semanticModel, data.DescendantNodesToAnalyze, cancellationToken);
            return data;
        }
 
        private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymbol declaredSymbol, SyntaxNode declaringReferenceSyntax, SyntaxNode topmostNodeForAnalysis, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken)
        {
            // We only care about the top level symbol declaration and its immediate member declarations.
            int? levelsToCompute = 2;
            var getSymbol = topmostNodeForAnalysis != declaringReferenceSyntax || declaredSymbol.Kind == SymbolKind.Namespace;
            semanticModel.ComputeDeclarationsInNode(topmostNodeForAnalysis, declaredSymbol, getSymbol, builder, cancellationToken, levelsToCompute);
        }
 
        /// <summary>
        /// Execute syntax node, code block and operation actions for the given declaration.
        /// </summary>
        private void ExecuteDeclaringReferenceActions(
            SyntaxReference decl,
            SymbolDeclaredCompilationEvent symbolEvent,
            AnalysisScope analysisScope,
            GroupedAnalyzerActions coreActions,
            GroupedAnalyzerActions additionalPerSymbolActions,
            bool shouldExecuteSyntaxNodeActions,
            bool shouldExecuteOperationActions,
            bool shouldExecuteCodeBlockActions,
            bool shouldExecuteOperationBlockActions,
            bool isInGeneratedCode,
            CancellationToken cancellationToken)
        {
            Debug.Assert(shouldExecuteSyntaxNodeActions || shouldExecuteOperationActions || shouldExecuteCodeBlockActions || shouldExecuteOperationBlockActions);
            Debug.Assert(!isInGeneratedCode || !DoNotAnalyzeGeneratedCode);
 
            var symbol = symbolEvent.Symbol;
 
            var semanticModel = symbolEvent.SemanticModelWithCachedBoundNodes ??
                GetOrCreateSemanticModel(decl.SyntaxTree, symbolEvent.Compilation);
 
            var declarationAnalysisData = ComputeDeclarationAnalysisData(symbol, decl, semanticModel, analysisScope, cancellationToken);
            if (analysisScope.ShouldAnalyze(declarationAnalysisData.TopmostNodeForAnalysis))
            {
                // Execute stateless syntax node actions.
                executeNodeActions();
 
                // Execute actions in executable code: code block actions, operation actions and operation block actions.
                executeExecutableCodeActions();
            }
 
            declarationAnalysisData.Free();
 
            return;
 
            void executeNodeActions()
            {
                if (shouldExecuteSyntaxNodeActions)
                {
                    var nodesToAnalyze = declarationAnalysisData.DescendantNodesToAnalyze;
                    executeNodeActionsByKind(nodesToAnalyze, coreActions, arePerSymbolActions: false);
                    executeNodeActionsByKind(nodesToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
                }
            }
 
            void executeNodeActionsByKind(ArrayBuilder<SyntaxNode> nodesToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
            {
                foreach (var (analyzer, groupedActionsForAnalyzer) in groupedActions.GroupedActionsByAnalyzer)
                {
                    var nodeActionsByKind = groupedActionsForAnalyzer.NodeActionsByAnalyzerAndKind;
                    if (nodeActionsByKind.IsEmpty || !analysisScope.Contains(analyzer))
                    {
                        continue;
                    }
 
                    // We further filter out the nodes to analyze based on analysis scope if we are performing
                    // partial analysis of the declaration, i.e. analyzing a sub-span within the declaration span,
                    // and additionally the analyzer has not registered any code block start actions. In case
                    // the analyzer has registered code block start actions, we need to make callbacks for all nodes
                    // in the code block to ensure the analyzer can correctly report code block end diagnostics.
                    if (declarationAnalysisData.IsPartialAnalysis && !groupedActionsForAnalyzer.HasCodeBlockStartActions)
                    {
                        var filteredNodesToAnalyze = ArrayBuilder<SyntaxNode>.GetInstance(nodesToAnalyze.Count);
                        foreach (var node in nodesToAnalyze)
                        {
                            if (analysisScope.ShouldAnalyze(node))
                                filteredNodesToAnalyze.Add(node);
                        }
 
                        executeSyntaxNodeActions(analyzer, groupedActionsForAnalyzer, filteredNodesToAnalyze);
                        filteredNodesToAnalyze.Free();
                    }
                    else
                    {
                        executeSyntaxNodeActions(analyzer, groupedActionsForAnalyzer, nodesToAnalyze);
                    }
                }
 
                void executeSyntaxNodeActions(
                    DiagnosticAnalyzer analyzer,
                    GroupedAnalyzerActionsForAnalyzer groupedActionsForAnalyzer,
                    ArrayBuilder<SyntaxNode> filteredNodesToAnalyze)
                {
                    AnalyzerExecutor.ExecuteSyntaxNodeActions(
                        filteredNodesToAnalyze, groupedActionsForAnalyzer.NodeActionsByAnalyzerAndKind,
                        analyzer, semanticModel, _getKind, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan,
                        symbol, analysisScope.FilterSpanOpt, isInGeneratedCode, hasCodeBlockStartOrSymbolStartActions: groupedActionsForAnalyzer.HasCodeBlockStartActions || arePerSymbolActions,
                        cancellationToken);
                }
            }
 
            void executeExecutableCodeActions()
            {
                if (!shouldExecuteCodeBlockActions && !shouldExecuteOperationActions && !shouldExecuteOperationBlockActions)
                {
                    return;
                }
 
                // Compute the executable code blocks of interest.
                var executableCodeBlocks = ImmutableArray<SyntaxNode>.Empty;
                var executableCodeBlockActionsBuilder = ArrayBuilder<ExecutableCodeBlockAnalyzerActions>.GetInstance();
                try
                {
                    foreach (var declInNode in declarationAnalysisData.DeclarationsInNode)
                    {
                        if (declInNode.DeclaredNode == declarationAnalysisData.TopmostNodeForAnalysis || declInNode.DeclaredNode == declarationAnalysisData.DeclaringReferenceSyntax)
                        {
                            executableCodeBlocks = declInNode.ExecutableCodeBlocks;
                            if (!executableCodeBlocks.IsEmpty)
                            {
                                if (shouldExecuteCodeBlockActions || shouldExecuteOperationBlockActions)
                                {
                                    addExecutableCodeBlockAnalyzerActions(coreActions, analysisScope, executableCodeBlockActionsBuilder);
                                    addExecutableCodeBlockAnalyzerActions(additionalPerSymbolActions, analysisScope, executableCodeBlockActionsBuilder);
                                }
 
                                // Execute operation actions.
                                if (shouldExecuteOperationActions || shouldExecuteOperationBlockActions)
                                {
                                    var operationBlocksToAnalyze = GetOperationBlocksToAnalyze(executableCodeBlocks, semanticModel, cancellationToken);
                                    var operationsToAnalyze = getOperationsToAnalyzeWithStackGuard(operationBlocksToAnalyze);
 
                                    if (!operationsToAnalyze.IsEmpty)
                                    {
                                        try
                                        {
                                            executeOperationsActions(operationsToAnalyze);
                                            executeOperationsBlockActions(operationBlocksToAnalyze, operationsToAnalyze, executableCodeBlockActionsBuilder);
                                        }
                                        finally
                                        {
                                            AnalyzerExecutor.OnOperationBlockActionsExecuted(operationBlocksToAnalyze);
                                        }
                                    }
                                }
 
                                break;
                            }
                        }
                    }
 
                    executeCodeBlockActions(executableCodeBlocks, executableCodeBlockActionsBuilder);
                }
                finally
                {
                    executableCodeBlockActionsBuilder.Free();
                }
            }
 
            ImmutableArray<IOperation> getOperationsToAnalyzeWithStackGuard(ImmutableArray<IOperation> operationBlocksToAnalyze)
            {
                try
                {
                    return GetOperationsToAnalyze(operationBlocksToAnalyze);
                }
                catch (Exception ex) when (ex is InsufficientExecutionStackException)
                {
                    var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(ex);
                    var analyzer = this.Analyzers[0];
 
                    AnalyzerExecutor.OnAnalyzerException(ex, analyzer, diagnostic, cancellationToken);
                    return ImmutableArray<IOperation>.Empty;
                }
            }
 
            void executeOperationsActions(ImmutableArray<IOperation> operationsToAnalyze)
            {
                if (shouldExecuteOperationActions)
                {
                    executeOperationsActionsByKind(operationsToAnalyze, coreActions, arePerSymbolActions: false);
                    executeOperationsActionsByKind(operationsToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
                }
            }
 
            void executeOperationsActionsByKind(ImmutableArray<IOperation> operationsToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
            {
                foreach (var (analyzer, groupedActionsForAnalyzer) in groupedActions.GroupedActionsByAnalyzer)
                {
                    var operationActionsByKind = groupedActionsForAnalyzer.OperationActionsByAnalyzerAndKind;
                    if (operationActionsByKind.IsEmpty || !analysisScope.Contains(analyzer))
                    {
                        continue;
                    }
 
                    // We further filter out the operation to analyze based on analysis scope if we are performing
                    // partial analysis of the declaration, i.e. analyzing a sub-span within the declaration span,
                    // and additionally the analyzer has not registered any operation block start actions. In case the
                    // analyzer has registered operation block start actions, we need to make callbacks for all operations
                    // in the operation block to ensure the analyzer can correctly report operation block end diagnostics.
                    var filteredOperationsToAnalyze = declarationAnalysisData.IsPartialAnalysis && !groupedActionsForAnalyzer.HasOperationBlockStartActions
                        ? operationsToAnalyze.WhereAsArray(operation => analysisScope.ShouldAnalyze(operation.Syntax))
                        : operationsToAnalyze;
 
                    AnalyzerExecutor.ExecuteOperationActions(filteredOperationsToAnalyze, operationActionsByKind,
                        analyzer, semanticModel, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan,
                        symbol, analysisScope.FilterSpanOpt, isInGeneratedCode, hasOperationBlockStartOrSymbolStartActions: groupedActionsForAnalyzer.HasOperationBlockStartActions || arePerSymbolActions,
                        cancellationToken);
                }
            }
 
            void executeOperationsBlockActions(ImmutableArray<IOperation> operationBlocksToAnalyze, ImmutableArray<IOperation> operationsToAnalyze, IEnumerable<ExecutableCodeBlockAnalyzerActions> codeBlockActions)
            {
                if (!shouldExecuteOperationBlockActions)
                {
                    return;
                }
 
                foreach (var analyzerActions in codeBlockActions)
                {
                    if (analyzerActions.OperationBlockStartActions.IsEmpty &&
                        analyzerActions.OperationBlockActions.IsEmpty &&
                        analyzerActions.OperationBlockEndActions.IsEmpty)
                    {
                        continue;
                    }
 
                    if (!analysisScope.Contains(analyzerActions.Analyzer))
                    {
                        continue;
                    }
 
                    AnalyzerExecutor.ExecuteOperationBlockActions(
                        analyzerActions.OperationBlockStartActions, analyzerActions.OperationBlockActions,
                        analyzerActions.OperationBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol,
                        operationBlocksToAnalyze, operationsToAnalyze, semanticModel, analysisScope.FilterSpanOpt, isInGeneratedCode, cancellationToken);
                }
            }
 
            void executeCodeBlockActions(ImmutableArray<SyntaxNode> executableCodeBlocks, IEnumerable<ExecutableCodeBlockAnalyzerActions> codeBlockActions)
            {
                if (executableCodeBlocks.IsEmpty || !shouldExecuteCodeBlockActions)
                {
                    return;
                }
 
                foreach (var analyzerActions in codeBlockActions)
                {
                    if (analyzerActions.CodeBlockStartActions.IsEmpty &&
                        analyzerActions.CodeBlockActions.IsEmpty &&
                        analyzerActions.CodeBlockEndActions.IsEmpty)
                    {
                        continue;
                    }
 
                    if (!analysisScope.Contains(analyzerActions.Analyzer))
                    {
                        continue;
                    }
 
                    AnalyzerExecutor.ExecuteCodeBlockActions(
                        analyzerActions.CodeBlockStartActions, analyzerActions.CodeBlockActions,
                        analyzerActions.CodeBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol,
                        executableCodeBlocks, semanticModel, _getKind, analysisScope.FilterSpanOpt, isInGeneratedCode, cancellationToken);
                }
            }
 
            static void addExecutableCodeBlockAnalyzerActions(
                GroupedAnalyzerActions groupedActions,
                AnalysisScope analysisScope,
                ArrayBuilder<ExecutableCodeBlockAnalyzerActions> builder)
            {
                foreach (var (analyzer, groupedActionsForAnalyzer) in groupedActions.GroupedActionsByAnalyzer)
                {
                    if (analysisScope.Contains(analyzer) &&
                        groupedActionsForAnalyzer.TryGetExecutableCodeBlockActions(out var executableCodeBlockActions))
                    {
                        builder.Add(executableCodeBlockActions);
                    }
                }
            }
        }
 
        private static void AddSyntaxNodesToAnalyze(
            SyntaxNode declaredNode,
            ISymbol declaredSymbol,
            ImmutableArray<DeclarationInfo> declarationsInNode,
            SemanticModel semanticModel,
            ArrayBuilder<SyntaxNode> nodesToAnalyze,
            CancellationToken cancellationToken)
        {
            // Eliminate descendant member declarations within declarations.
            // There will be separate symbols declared for the members.
            HashSet<SyntaxNode>? descendantDeclsToSkip = null;
            bool first = true;
            foreach (var declInNode in declarationsInNode)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (declInNode.DeclaredNode != declaredNode)
                {
                    // Might be:
                    // (1) A field declaration statement with multiple fields declared.
                    //     If so, we execute syntax node analysis for entire field declaration (and its descendants)
                    //     if we processing the first field and skip syntax actions for remaining fields in the declaration.
                    // (2) A namespace declaration statement with qualified name "namespace A.B { }"
                    if (IsEquivalentSymbol(declaredSymbol, declInNode.DeclaredSymbol))
                    {
                        if (first)
                        {
                            break;
                        }
 
                        return;
                    }
 
                    // Compute the topmost node representing the syntax declaration for the member that needs to be skipped.
                    var declarationNodeToSkip = declInNode.DeclaredNode;
                    var declaredSymbolOfDeclInNode = declInNode.DeclaredSymbol ?? semanticModel.GetDeclaredSymbol(declInNode.DeclaredNode, cancellationToken);
                    if (declaredSymbolOfDeclInNode != null)
                    {
                        declarationNodeToSkip = semanticModel.GetTopmostNodeForDiagnosticAnalysis(declaredSymbolOfDeclInNode, declInNode.DeclaredNode);
                    }
 
                    descendantDeclsToSkip ??= new HashSet<SyntaxNode>();
                    descendantDeclsToSkip.Add(declarationNodeToSkip);
                }
 
                first = false;
            }
 
            Func<SyntaxNode, bool>? additionalFilter = semanticModel.GetSyntaxNodesToAnalyzeFilter(declaredNode, declaredSymbol);
            bool shouldAddNode(SyntaxNode node) => (descendantDeclsToSkip == null || !descendantDeclsToSkip.Contains(node)) && (additionalFilter is null || additionalFilter(node));
            foreach (var node in declaredNode.DescendantNodesAndSelf(descendIntoChildren: shouldAddNode, descendIntoTrivia: true))
            {
                if (shouldAddNode(node) &&
                    !semanticModel.ShouldSkipSyntaxNodeAnalysis(node, declaredSymbol))
                {
                    nodesToAnalyze.Add(node);
                }
            }
        }
 
        private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol? otherSymbol)
        {
            if (declaredSymbol.Equals(otherSymbol))
            {
                return true;
            }
 
            // GetSymbolInfo(name syntax) for "A" in "namespace A.B { }" sometimes returns a symbol which doesn't match
            // the symbol declared in the compilation. So we do an equivalence check for such namespace symbols.
            return otherSymbol != null &&
                declaredSymbol.Kind == SymbolKind.Namespace &&
                otherSymbol.Kind == SymbolKind.Namespace &&
                declaredSymbol.Name == otherSymbol.Name &&
                declaredSymbol.ToDisplayString() == otherSymbol.ToDisplayString();
        }
 
        private static ImmutableArray<IOperation> GetOperationBlocksToAnalyze(
            ImmutableArray<SyntaxNode> executableBlocks,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            ArrayBuilder<IOperation> operationBlocksToAnalyze = ArrayBuilder<IOperation>.GetInstance();
 
            foreach (SyntaxNode executableBlock in executableBlocks)
            {
                if (semanticModel.GetOperation(executableBlock, cancellationToken) is { } operation)
                {
                    operationBlocksToAnalyze.AddRange(operation);
                }
            }
 
            return operationBlocksToAnalyze.ToImmutableAndFree();
        }
 
        private static ImmutableArray<IOperation> GetOperationsToAnalyze(
            ImmutableArray<IOperation> operationBlocks)
        {
            ArrayBuilder<IOperation> operationsToAnalyze = ArrayBuilder<IOperation>.GetInstance();
            var checkParent = true;
 
            foreach (IOperation operationBlock in operationBlocks)
            {
                if (checkParent)
                {
                    // Special handling for IMethodBodyOperation and IConstructorBodyOperation.
                    // These are newly added root operation nodes for C# method and constructor bodies.
                    // However, to avoid a breaking change for existing operation block analyzers,
                    // we have decided to retain the current behavior of making operation block callbacks with the contained
                    // method body and/or constructor initializer operation nodes.
                    // Hence we detect here if the operation block is parented by IMethodBodyOperation or IConstructorBodyOperation and
                    // add them to 'operationsToAnalyze' so that analyzers that explicitly register for these operation kinds
                    // can get callbacks for these nodes.
                    if (operationBlock.Parent != null)
                    {
                        switch (operationBlock.Parent.Kind)
                        {
                            case OperationKind.MethodBody:
                            case OperationKind.ConstructorBody:
                                Debug.Assert(!operationBlock.Parent.IsImplicit);
                                operationsToAnalyze.Add(operationBlock.Parent);
                                break;
 
                            case OperationKind.ExpressionStatement:
                                // For constructor initializer, we generate an IInvocationOperation (or invalid
                                // operation in the case of an error) with an implicit IExpressionStatementOperation parent.
                                Debug.Assert(operationBlock.Kind is OperationKind.Invocation or OperationKind.Invalid);
                                Debug.Assert(operationBlock.Parent.IsImplicit);
                                Debug.Assert(operationBlock.Parent.Parent is IConstructorBodyOperation ctorBody &&
                                    ctorBody.Initializer == operationBlock.Parent);
                                Debug.Assert(!operationBlock.Parent.Parent.IsImplicit);
 
                                operationsToAnalyze.Add(operationBlock.Parent.Parent);
 
                                break;
 
                            default:
                                Debug.Fail($"Expected operation with kind '{operationBlock.Kind}' to be the root operation with null 'Parent', but instead it has a non-null Parent with kind '{operationBlock.Parent.Kind}'");
                                break;
                        }
 
                        checkParent = false;
                    }
                }
 
                operationsToAnalyze.AddRange(operationBlock.DescendantsAndSelf());
            }
 
            Debug.Assert(operationsToAnalyze.ToImmutableHashSet().Count == operationsToAnalyze.Count);
            return operationsToAnalyze.ToImmutableAndFree();
        }
    }
}