File: DiagnosticAnalyzer\DiagnosticAnalysisContext.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.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    /// <summary>
    /// Context for initializing an analyzer.
    /// Analyzer initialization can use an <see cref="AnalysisContext"/> to register actions to be executed at any of:
    /// <list type="bullet">
    /// <item>
    /// <description>compilation start,</description>
    /// </item>
    /// <item>
    /// <description>compilation end,</description>
    /// </item>
    /// <item>
    /// <description>completion of parsing a code document,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a code document,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a symbol,</description>
    /// </item>
    /// <item>
    /// <description>start of semantic analysis of a method body or an expression appearing outside a method body,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a method body or an expression appearing outside a method body, or</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a syntax node.</description>
    /// </item>
    /// </list>
    /// </summary>
    public abstract class AnalysisContext
    {
        /// <summary>
        /// Register an action to be executed at compilation start.
        /// A compilation start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <param name="action">Action to be executed at compilation start.</param>
        public abstract void RegisterCompilationStartAction(Action<CompilationStartAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed for a complete compilation.
        /// A compilation action reports <see cref="Diagnostic"/>s about the <see cref="Compilation"/>.
        /// </summary>
        /// <param name="action">Action to be executed at compilation end.</param>
        public abstract void RegisterCompilationAction(Action<CompilationAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a document,
        /// which will operate on the <see cref="SemanticModel"/> of the document. A semantic model action
        /// reports <see cref="Diagnostic"/>s about the model.
        /// </summary>
        /// <param name="action">Action to be executed for a document's <see cref="SemanticModel"/>.</param>
        public abstract void RegisterSemanticModelAction(Action<SemanticModelAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="ISymbol"/> with an appropriate Kind.
        /// A symbol action reports <see cref="Diagnostic"/>s about <see cref="ISymbol"/>s.
        /// </summary>
        /// <param name="action">Action to be executed for an <see cref="ISymbol"/>.</param>
        /// <param name="symbolKinds">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches one of the <see cref="SymbolKind"/> values.</param>
        public void RegisterSymbolAction(Action<SymbolAnalysisContext> action, params SymbolKind[] symbolKinds)
        {
            this.RegisterSymbolAction(action, symbolKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="ISymbol"/> with an appropriate Kind.
        /// A symbol action reports <see cref="Diagnostic"/>s about <see cref="ISymbol"/>s.
        /// </summary>
        /// <param name="action">Action to be executed for an <see cref="ISymbol"/>.</param>
        /// <param name="symbolKinds">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches one of the <see cref="SymbolKind"/> values.</param>
        public abstract void RegisterSymbolAction(Action<SymbolAnalysisContext> action, ImmutableArray<SymbolKind> symbolKinds);
 
        /// <summary>
        /// Register an action to be executed at start of semantic analysis of an <see cref="ISymbol"/> and its members with an appropriate Kind.
        /// </summary>
        /// <param name="action">Action to be executed.</param>
        /// <param name="symbolKind">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches the given <see cref="SymbolKind"/>.</param>
        public virtual void RegisterSymbolStartAction(Action<SymbolStartAnalysisContext> action, SymbolKind symbolKind)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// A code block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at the start of semantic analysis of a code block.</param>
        public abstract void RegisterCodeBlockStartAction<TLanguageKindEnum>(Action<CodeBlockStartAnalysisContext<TLanguageKindEnum>> action) where TLanguageKindEnum : struct;
 
        /// <summary> 
        /// Register an action to be executed after semantic analysis of a method body or an expression appearing outside a method body. 
        /// A code block action reports <see cref="Diagnostic"/>s about code blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for a code block.</param> 
        public abstract void RegisterCodeBlockAction(Action<CodeBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of parsing of a code document.
        /// A syntax tree action reports <see cref="Diagnostic"/>s about the <see cref="SyntaxTree"/> of a document.
        /// </summary>
        /// <param name="action">Action to be executed at completion of parsing of a document.</param>
        public abstract void RegisterSyntaxTreeAction(Action<SyntaxTreeAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed for each non-code document.
        /// An additional file action reports <see cref="Diagnostic"/>s about the <see cref="AdditionalText"/> of a document.
        /// </summary>
        /// <param name="action">Action to be executed for each non-code document.</param>
        public virtual void RegisterAdditionalFileAction(Action<AdditionalFileAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds) where TLanguageKindEnum : struct
        {
            this.RegisterSyntaxNodeAction(action, syntaxKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public abstract void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, ImmutableArray<TLanguageKindEnum> syntaxKinds) where TLanguageKindEnum : struct;
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// An operation block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <param name="action">Action to be executed at the start of semantic analysis of an operation block.</param>
        public virtual void RegisterOperationBlockStartAction(Action<OperationBlockStartAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary> 
        /// Register an action to be executed after semantic analysis of a method body or an expression appearing outside a method body. 
        /// An operation block action reports <see cref="Diagnostic"/>s about operation blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for an operation block.</param> 
        public virtual void RegisterOperationBlockAction(Action<OperationBlockAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public void RegisterOperationAction(Action<OperationAnalysisContext> action, params OperationKind[] operationKinds)
        {
            this.RegisterOperationAction(action, operationKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public virtual void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Enable concurrent execution of analyzer actions registered by this analyzer.
        /// An analyzer that registers for concurrent execution can have better performance than a non-concurrent analyzer.
        /// However, such an analyzer must ensure that its actions can execute correctly in parallel.
        /// </summary>
        /// <remarks>
        /// Even when an analyzer registers for concurrent execution, certain related actions are *never* executed concurrently.
        /// For example, end actions registered on any analysis unit (compilation, code block, operation block, etc.) are by definition semantically dependent on analysis from non-end actions registered on the same analysis unit.
        /// Hence, end actions are never executed concurrently with non-end actions operating on the same analysis unit.
        /// </remarks>
        public virtual void EnableConcurrentExecution()
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Configure analysis mode of generated code for this analyzer.
        /// Non-configured analyzers will default to an appropriate default mode for generated code.
        /// It is recommended for the analyzer to invoke this API with the required <see cref="GeneratedCodeAnalysisFlags"/> setting.
        /// </summary>
        public virtual void ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags analysisMode)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Indicates the minimum reported diagnostic severity for this analysis context.
        /// Analyzer diagnostics with severity lesser than this severity are not reported.
        /// </summary>
        public virtual DiagnosticSeverity MinimumReportedSeverity => DiagnosticSeverity.Hidden;
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="SourceText"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(SourceText text, SourceTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="AdditionalText"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(AdditionalText text, AdditionalTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        private bool TryGetValue<TKey, TValue>(TKey key, AnalysisValueProvider<TKey, TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
            where TKey : class
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(key, valueProvider);
            return valueProvider.TryGetValue(key, out value);
        }
    }
 
    /// <summary>
    /// Flags to configure mode of generated code analysis.
    /// </summary>
    [Flags]
    public enum GeneratedCodeAnalysisFlags
    {
        /// <summary>
        /// Disable analyzer action callbacks and diagnostic reporting for generated code.
        /// Analyzer driver will not make callbacks into the analyzer for entities (source files, symbols, etc.) that it classifies as generated code.
        /// Additionally, any diagnostic reported by the analyzer with location in generated code will not be reported.
        /// </summary>
        None = 0x00,
 
        /// <summary>
        /// Enable analyzer action callbacks for generated code.
        /// Analyzer driver will make callbacks into the analyzer for all entities (source files, symbols, etc.) in the compilation, including generated code.
        /// </summary>
        Analyze = 0x01,
 
        /// <summary>
        /// Enable reporting diagnostics on generated code.
        /// Analyzer driver will not suppress any analyzer diagnostic based on whether or not it's location is in generated code.
        /// </summary>
        ReportDiagnostics = 0x02,
    }
 
    /// <summary>
    /// Context for a compilation start action.
    /// A compilation start action can use a <see cref="CompilationStartAnalysisContext"/> to register actions to be executed at any of:
    /// <list type="bullet">
    /// <item>
    /// <description>compilation end,</description>
    /// </item>
    /// <item>
    /// <description>completion of parsing a code document,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a code document,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a symbol,</description>
    /// </item>
    /// <item>
    /// <description>start of semantic analysis of a method body or an expression appearing outside a method body,</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a method body or an expression appearing outside a method body, or</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a syntax node.</description>
    /// </item>
    /// </list>
    /// </summary>
    public abstract class CompilationStartAnalysisContext
    {
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> that is the subject of the analysis.
        /// </summary>
        public Compilation Compilation { get { return _compilation; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        protected CompilationStartAnalysisContext(Compilation compilation, AnalyzerOptions options, CancellationToken cancellationToken)
        {
            _compilation = compilation;
            _options = options;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Register an action to be executed at compilation end.
        /// A compilation end action reports <see cref="Diagnostic"/>s about the <see cref="CodeAnalysis.Compilation"/>.
        /// </summary>
        /// <param name="action">Action to be executed at compilation end.</param>
        public abstract void RegisterCompilationEndAction(Action<CompilationAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a document,
        /// which will operate on the <see cref="SemanticModel"/> of the document. A semantic model action
        /// reports <see cref="Diagnostic"/>s about the model.
        /// </summary>
        /// <param name="action">Action to be executed for a document's <see cref="SemanticModel"/>.</param>
        public abstract void RegisterSemanticModelAction(Action<SemanticModelAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="ISymbol"/> with an appropriate Kind.
        /// A symbol action reports <see cref="Diagnostic"/>s about <see cref="ISymbol"/>s.
        /// </summary>
        /// <param name="action">Action to be executed for an <see cref="ISymbol"/>.</param>
        /// <param name="symbolKinds">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches one of the <see cref="SymbolKind"/> values.</param>
        public void RegisterSymbolAction(Action<SymbolAnalysisContext> action, params SymbolKind[] symbolKinds)
        {
            this.RegisterSymbolAction(action, symbolKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="ISymbol"/> with an appropriate Kind.
        /// A symbol action reports <see cref="Diagnostic"/>s about <see cref="ISymbol"/>s.
        /// </summary>
        /// <param name="action">Action to be executed for an <see cref="ISymbol"/>.</param>
        /// <param name="symbolKinds">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches one of the <see cref="SymbolKind"/> values.</param>
        public abstract void RegisterSymbolAction(Action<SymbolAnalysisContext> action, ImmutableArray<SymbolKind> symbolKinds);
 
        /// <summary>
        /// Register an action to be executed at start of semantic analysis of an <see cref="ISymbol"/> and its members with an appropriate Kind.
        /// </summary>
        /// <param name="action">Action to be executed.</param>
        /// <param name="symbolKind">Action will be executed only if an <see cref="ISymbol"/>'s Kind matches the given <see cref="SymbolKind"/>.</param>
        public virtual void RegisterSymbolStartAction(Action<SymbolStartAnalysisContext> action, SymbolKind symbolKind)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// A code block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at the start of semantic analysis of a code block.</param>
        public abstract void RegisterCodeBlockStartAction<TLanguageKindEnum>(Action<CodeBlockStartAnalysisContext<TLanguageKindEnum>> action) where TLanguageKindEnum : struct;
 
        /// <summary> 
        /// Register an action to be executed at the end of semantic analysis of a method body or an expression appearing outside a method body. 
        /// A code block action reports <see cref="Diagnostic"/>s about code blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for a code block.</param> 
        public abstract void RegisterCodeBlockAction(Action<CodeBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// An operation block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <param name="action">Action to be executed at the start of semantic analysis of an operation block.</param>
        public virtual void RegisterOperationBlockStartAction(Action<OperationBlockStartAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary> 
        /// Register an action to be executed after semantic analysis of a method body or an expression appearing outside a method body. 
        /// An operation block action reports <see cref="Diagnostic"/>s about operation blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for an operation block.</param> 
        public virtual void RegisterOperationBlockAction(Action<OperationBlockAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at completion of parsing of a code document.
        /// A syntax tree action reports <see cref="Diagnostic"/>s about the <see cref="SyntaxTree"/> of a document.
        /// </summary>
        /// <param name="action">Action to be executed at completion of parsing of a document.</param>
        public abstract void RegisterSyntaxTreeAction(Action<SyntaxTreeAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed for each non-code document.
        /// An additional file action reports <see cref="Diagnostic"/>s about the <see cref="AdditionalText"/> of a document.
        /// </summary>
        /// <param name="action">Action to be executed for each non-code document.</param>
        public virtual void RegisterAdditionalFileAction(Action<AdditionalFileAnalysisContext> action)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds) where TLanguageKindEnum : struct
        {
            this.RegisterSyntaxNodeAction(action, syntaxKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public abstract void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, ImmutableArray<TLanguageKindEnum> syntaxKinds) where TLanguageKindEnum : struct;
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public void RegisterOperationAction(Action<OperationAnalysisContext> action, params OperationKind[] operationKinds)
        {
            this.RegisterOperationAction(action, operationKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public virtual void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="SourceText"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(SourceText text, SourceTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="AdditionalText"/> instance for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(AdditionalText text, AdditionalTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="tree"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="tree"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="tree"><see cref="SyntaxTree"/> instance for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(SyntaxTree tree, SyntaxTreeValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(tree, valueProvider.CoreValueProvider, out value);
        }
 
        private bool TryGetValue<TKey, TValue>(TKey key, AnalysisValueProvider<TKey, TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
            where TKey : class
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(key, valueProvider);
            return TryGetValueCore(key, valueProvider, out value);
        }
 
        internal virtual bool TryGetValueCore<TKey, TValue>(TKey key, AnalysisValueProvider<TKey, TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
            where TKey : class
        {
            throw new NotImplementedException();
        }
    }
 
    /// <summary>
    /// Context for a compilation action or compilation end action.
    /// A compilation action or compilation end action can use a <see cref="CompilationAnalysisContext"/> to report <see cref="Diagnostic"/>s about a <see cref="CodeAnalysis.Compilation"/>.
    /// </summary>
    public readonly struct CompilationAnalysisContext
    {
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CompilationAnalysisValueProviderFactory? _compilationAnalysisValueProviderFactoryOpt;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> that is the subject of the analysis.
        /// </summary>
        public Compilation Compilation { get { return _compilation; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public CompilationAnalysisContext(Compilation compilation, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(compilation, options, reportDiagnostic, isSupportedDiagnostic: (d, c) => isSupportedDiagnostic(d), null, cancellationToken)
        {
        }
 
        internal CompilationAnalysisContext(
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            CompilationAnalysisValueProviderFactory? compilationAnalysisValueProviderFactoryOpt,
            CancellationToken cancellationToken)
        {
            _compilation = compilation;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            _compilationAnalysisValueProviderFactoryOpt = compilationAnalysisValueProviderFactoryOpt;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a <see cref="CodeAnalysis.Compilation"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="SourceText"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(SourceText text, SourceTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="text"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="text"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="text"><see cref="AdditionalText"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(AdditionalText text, AdditionalTextValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(text, valueProvider.CoreValueProvider, out value);
        }
 
        /// <summary>
        /// Attempts to compute or get the cached value provided by the given <paramref name="valueProvider"/> for the given <paramref name="tree"/>.
        /// Note that the pair {<paramref name="valueProvider"/>, <paramref name="tree"/>} acts as the key.
        /// Reusing the same <paramref name="valueProvider"/> instance across analyzer actions and/or analyzer instances can improve the overall analyzer performance by avoiding recomputation of the values.
        /// </summary>
        /// <typeparam name="TValue">The type of the value associated with the key.</typeparam>
        /// <param name="tree"><see cref="SyntaxTree"/> for which the value is queried.</param>
        /// <param name="valueProvider">Provider that computes the underlying value.</param>
        /// <param name="value">Value associated with the key.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool TryGetValue<TValue>(SyntaxTree tree, SyntaxTreeValueProvider<TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
        {
            return TryGetValue(tree, valueProvider.CoreValueProvider, out value);
        }
 
        private bool TryGetValue<TKey, TValue>(TKey key, AnalysisValueProvider<TKey, TValue> valueProvider, [MaybeNullWhen(false)] out TValue value)
            where TKey : class
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(key, valueProvider);
 
            if (_compilationAnalysisValueProviderFactoryOpt != null)
            {
                var compilationAnalysisValueProvider = _compilationAnalysisValueProviderFactoryOpt.GetValueProvider(valueProvider);
                return compilationAnalysisValueProvider.TryGetValue(key, out value);
            }
 
            return valueProvider.TryGetValue(key, out value);
        }
    }
 
    /// <summary>
    /// Context for a semantic model action.
    /// A semantic model action operates on the <see cref="CodeAnalysis.SemanticModel"/> of a code document, and can use a <see cref="SemanticModelAnalysisContext"/> to report <see cref="Diagnostic"/>s about the model.
    /// </summary>
    public readonly struct SemanticModelAnalysisContext
    {
        private readonly SemanticModel _semanticModel;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="CodeAnalysis.SemanticModel"/> that is the subject of the analysis.
        /// </summary>
        public SemanticModel SemanticModel { get { return _semanticModel; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        /// <summary>
        /// Syntax tree for the <see cref="SemanticModel"/> being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the underlying <see cref="SemanticModel.SyntaxTree"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(semanticModel, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal SemanticModelAnalysisContext(
            SemanticModel semanticModel,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _semanticModel = semanticModel;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            FilterTree = semanticModel.SyntaxTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a <see cref="CodeAnalysis.SemanticModel"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _semanticModel.Compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for a symbol action.
    /// A symbol action can use a <see cref="SymbolAnalysisContext"/> to report <see cref="Diagnostic"/>s about an <see cref="ISymbol"/>.
    /// </summary>
    public readonly struct SymbolAnalysisContext
    {
        private readonly ISymbol _symbol;
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="ISymbol"/> that is the subject of the analysis.
        /// </summary>
        public ISymbol Symbol { get { return _symbol; } }
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="ISymbol"/>.
        /// </summary>
        public Compilation Compilation { get { return _compilation; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Optional filter tree being analyzed.
        /// <see langword="null"/> if we are analyzing the entire compilation.
        /// </summary>
        public SyntaxTree? FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        /// <remarks>This property is guaranteed to be <see langword="null"/> if <see cref="FilterTree"/> is <see langword="null"/>.</remarks>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        internal Func<Diagnostic, CancellationToken, bool> IsSupportedDiagnostic => _isSupportedDiagnostic;
 
        /// <summary>
        /// Indicates if the <see cref="Symbol"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(symbol, compilation, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), isGeneratedCode: false, filterTree: null, filterSpan: null, cancellationToken)
        {
        }
 
        internal SymbolAnalysisContext(
            ISymbol symbol,
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            bool isGeneratedCode,
            SyntaxTree? filterTree,
            TextSpan? filterSpan,
            CancellationToken cancellationToken)
        {
            Debug.Assert(!filterSpan.HasValue || filterTree != null);
 
            _symbol = symbol;
            _compilation = compilation;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            IsGeneratedCode = isGeneratedCode;
            FilterTree = filterTree;
            FilterSpan = filterSpan;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about an <see cref="ISymbol"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for a symbol start action to analyze a symbol and its members.
    /// A symbol start/end action can use a <see cref="SymbolStartAnalysisContext"/> to report <see cref="Diagnostic"/>s about code within a <see cref="ISymbol"/> and its members.
    /// </summary>
    public abstract class SymbolStartAnalysisContext
    {
        /// <summary>
        /// <see cref="ISymbol"/> that is the subject of the analysis.
        /// </summary>
        public ISymbol Symbol { get; }
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="ISymbol"/>.
        /// </summary>
        public Compilation Compilation { get; }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get; }
 
        /// <summary>
        /// Indicates if the <see cref="Symbol"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Optional filter tree being analyzed.
        /// <see langword="null"/> if we are analyzing the entire compilation.
        /// </summary>
        public SyntaxTree? FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        /// <remarks>This property is guaranteed to be <see langword="null"/> if <see cref="FilterTree"/> is <see langword="null"/>.</remarks>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get; }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SymbolStartAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, CancellationToken cancellationToken)
            : this(symbol, compilation, options, isGeneratedCode: false, filterTree: null, filterSpan: null, cancellationToken)
        {
        }
 
        internal SymbolStartAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, bool isGeneratedCode, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
        {
            Debug.Assert(!filterSpan.HasValue || filterTree != null);
 
            Symbol = symbol;
            Compilation = compilation;
            Options = options;
            IsGeneratedCode = isGeneratedCode;
            FilterTree = filterTree;
            FilterSpan = filterSpan;
            CancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Register an action to be executed at end of semantic analysis of an <see cref="ISymbol"/> and its members.
        /// A symbol end action reports <see cref="Diagnostic"/>s about the code within a <see cref="Symbol"/> and its members.
        /// </summary>
        /// <param name="action">Action to be executed at compilation end.</param>
        public abstract void RegisterSymbolEndAction(Action<SymbolAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// A code block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at the start of semantic analysis of a code block.</param>
        public abstract void RegisterCodeBlockStartAction<TLanguageKindEnum>(Action<CodeBlockStartAnalysisContext<TLanguageKindEnum>> action) where TLanguageKindEnum : struct;
 
        /// <summary> 
        /// Register an action to be executed after semantic analysis of a method body or an expression appearing outside a method body. 
        /// A code block action reports <see cref="Diagnostic"/>s about code blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for a code block.</param> 
        public abstract void RegisterCodeBlockAction(Action<CodeBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds) where TLanguageKindEnum : struct
        {
            this.RegisterSyntaxNodeAction(action, syntaxKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which the action applies.</typeparam>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public abstract void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNodeAnalysisContext> action, ImmutableArray<TLanguageKindEnum> syntaxKinds) where TLanguageKindEnum : struct;
 
        /// <summary>
        /// Register an action to be executed at the start of semantic analysis of a method body or an expression appearing outside a method body.
        /// An operation block start action can register other actions and/or collect state information to be used in diagnostic analysis,
        /// but cannot itself report any <see cref="Diagnostic"/>s.
        /// </summary>
        /// <param name="action">Action to be executed at the start of semantic analysis of an operation block.</param>
        public abstract void RegisterOperationBlockStartAction(Action<OperationBlockStartAnalysisContext> action);
 
        /// <summary> 
        /// Register an action to be executed after semantic analysis of a method body or an expression appearing outside a method body. 
        /// An operation block action reports <see cref="Diagnostic"/>s about operation blocks. 
        /// </summary> 
        /// <param name="action">Action to be executed for an operation block.</param> 
        public abstract void RegisterOperationBlockAction(Action<OperationBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public void RegisterOperationAction(Action<OperationAnalysisContext> action, params OperationKind[] operationKinds)
        {
            this.RegisterOperationAction(action, operationKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public abstract void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds);
    }
 
    /// <summary>
    /// Context for a code block start action.
    /// A code block start action can use a <see cref="CodeBlockStartAnalysisContext{TLanguageKindEnum}"/> to register actions to be executed
    /// at any of:
    /// <list type="bullet">
    /// <item>
    /// <description>completion of semantic analysis of a method body or an expression appearing outside a method body, or</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of a syntax node.</description>
    /// </item>
    /// </list>
    /// </summary>
    public abstract class CodeBlockStartAnalysisContext<TLanguageKindEnum> where TLanguageKindEnum : struct
    {
        private readonly SyntaxNode _codeBlock;
        private readonly ISymbol _owningSymbol;
        private readonly SemanticModel _semanticModel;
        private readonly AnalyzerOptions _options;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// Method body or expression subject to analysis.
        /// </summary>
        public SyntaxNode CodeBlock { get { return _codeBlock; } }
 
        /// <summary>
        /// <see cref="ISymbol"/> for which the code block provides a definition or value.
        /// </summary>
        public ISymbol OwningSymbol { get { return _owningSymbol; } }
 
        /// <summary>
        /// <see cref="CodeAnalysis.SemanticModel"/> that can provide semantic information about the <see cref="SyntaxNode"/>s in the code block.
        /// </summary>
        public SemanticModel SemanticModel { get { return _semanticModel; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Syntax tree corresponding to the code block being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="CodeBlock"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        protected CodeBlockStartAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, CancellationToken cancellationToken)
            : this(codeBlock, owningSymbol, semanticModel, options, filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        private protected CodeBlockStartAnalysisContext(
            SyntaxNode codeBlock,
            ISymbol owningSymbol,
            SemanticModel semanticModel,
            AnalyzerOptions options,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _codeBlock = codeBlock;
            _owningSymbol = owningSymbol;
            _semanticModel = semanticModel;
            _options = options;
            FilterTree = codeBlock.SyntaxTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Register an action to be executed at the end of semantic analysis of a method body or an expression appearing outside a method body.
        /// A code block end action reports <see cref="Diagnostic"/>s about code blocks.
        /// </summary>
        /// <param name="action">Action to be executed at the end of semantic analysis of a code block.</param>
        public abstract void RegisterCodeBlockEndAction(Action<CodeBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public void RegisterSyntaxNodeAction(Action<SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds)
        {
            this.RegisterSyntaxNodeAction(action, syntaxKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an appropriate Kind.
        /// A syntax node action can report <see cref="Diagnostic"/>s about <see cref="SyntaxNode"/>s, and can also collect
        /// state information to be used by other syntax node actions or code block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/>.</param>
        /// <param name="syntaxKinds">Action will be executed only if a <see cref="SyntaxNode"/>'s Kind matches one of the syntax kind values.</param>
        public abstract void RegisterSyntaxNodeAction(Action<SyntaxNodeAnalysisContext> action, ImmutableArray<TLanguageKindEnum> syntaxKinds);
    }
 
    /// <summary>
    /// Context for a code block action or code block end action.
    /// A code block action or code block end action can use a <see cref="CodeBlockAnalysisContext"/> to report <see cref="Diagnostic"/>s about a code block.
    /// </summary>
    public readonly struct CodeBlockAnalysisContext
    {
        private readonly SyntaxNode _codeBlock;
        private readonly ISymbol _owningSymbol;
        private readonly SemanticModel _semanticModel;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// Code block that is the subject of the analysis.
        /// </summary>
        public SyntaxNode CodeBlock { get { return _codeBlock; } }
 
        /// <summary>
        /// <see cref="ISymbol"/> for which the code block provides a definition or value.
        /// </summary>
        public ISymbol OwningSymbol { get { return _owningSymbol; } }
 
        /// <summary>
        /// <see cref="CodeAnalysis.SemanticModel"/> that can provide semantic information about the <see cref="SyntaxNode"/>s in the code block.
        /// </summary>
        public SemanticModel SemanticModel { get { return _semanticModel; } }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get { return _options; } }
 
        /// <summary>
        /// Syntax tree for the code block being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="CodeBlock"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get { return _cancellationToken; } }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(codeBlock, owningSymbol, semanticModel, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal CodeBlockAnalysisContext(
            SyntaxNode codeBlock,
            ISymbol owningSymbol,
            SemanticModel semanticModel,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _codeBlock = codeBlock;
            _owningSymbol = owningSymbol;
            _semanticModel = semanticModel;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            FilterTree = codeBlock.SyntaxTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a code block.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _semanticModel.Compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for an operation block start action.
    /// An operation block start action can use an <see cref="OperationBlockStartAnalysisContext"/> to register actions to be executed
    /// at any of:
    /// <list type="bullet">
    /// <item>
    /// <description>completion of semantic analysis of a method body or an expression appearing outside a method body, or</description>
    /// </item>
    /// <item>
    /// <description>completion of semantic analysis of an operation.</description>
    /// </item>
    /// </list>
    /// </summary>
    public abstract class OperationBlockStartAnalysisContext
    {
        private readonly ImmutableArray<IOperation> _operationBlocks;
        private readonly ISymbol _owningSymbol;
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly Func<IOperation, ControlFlowGraph>? _getControlFlowGraph;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// One or more operation blocks that are the subject of the analysis.
        /// This includes all blocks associated with the <see cref="OwningSymbol"/>,
        /// such as method body, field/property/constructor/parameter initializer(s), attributes, etc.
        /// </summary>
        /// <remarks>Note that the operation blocks are not in any specific order.</remarks>
        public ImmutableArray<IOperation> OperationBlocks => _operationBlocks;
 
        /// <summary>
        /// <see cref="ISymbol"/> for which the <see cref="OperationBlocks"/> provides a definition or value.
        /// </summary>
        public ISymbol OwningSymbol => _owningSymbol;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="OperationBlocks"/>.
        /// </summary>
        public Compilation Compilation => _compilation;
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options => _options;
 
        /// <summary>
        /// Syntax tree for the <see cref="OperationBlocks"/> being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="OperationBlocks"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken => _cancellationToken;
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        protected OperationBlockStartAnalysisContext(
            ImmutableArray<IOperation> operationBlocks,
            ISymbol owningSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            CancellationToken cancellationToken)
            : this(operationBlocks, owningSymbol, compilation, options, getControlFlowGraph: null,
                  filterTree: operationBlocks[0].Syntax.SyntaxTree, filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal OperationBlockStartAnalysisContext(
            ImmutableArray<IOperation> operationBlocks,
            ISymbol owningSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            Func<IOperation, ControlFlowGraph>? getControlFlowGraph,
            SyntaxTree filterTree,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _operationBlocks = operationBlocks;
            _owningSymbol = owningSymbol;
            _compilation = compilation;
            _options = options;
            _getControlFlowGraph = getControlFlowGraph;
            FilterTree = filterTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Register an action to be executed at the end of semantic analysis of a method body or an expression appearing outside a method body.
        /// A code block end action reports <see cref="Diagnostic"/>s about code blocks.
        /// </summary>
        /// <param name="action">Action to be executed at the end of semantic analysis of a code block.</param>
        public abstract void RegisterOperationBlockEndAction(Action<OperationBlockAnalysisContext> action);
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an operation with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or operation block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public void RegisterOperationAction(Action<OperationAnalysisContext> action, params OperationKind[] operationKinds)
        {
            this.RegisterOperationAction(action, operationKinds.AsImmutableOrEmpty());
        }
 
        /// <summary>
        /// Register an action to be executed at completion of semantic analysis of an <see cref="IOperation"/> with an appropriate Kind.
        /// An operation action can report <see cref="Diagnostic"/>s about <see cref="IOperation"/>s, and can also collect
        /// state information to be used by other operation actions or operation block end actions.
        /// </summary>
        /// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
        /// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
        public abstract void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds);
 
        /// <summary>
        /// Gets a <see cref="ControlFlowGraph"/> for a given <paramref name="operationBlock"/> from this analysis context's <see cref="OperationBlocks"/>.
        /// </summary>
        /// <param name="operationBlock">Operation block.</param>
        public ControlFlowGraph GetControlFlowGraph(IOperation operationBlock)
        {
            if (operationBlock == null)
            {
                throw new ArgumentNullException(nameof(operationBlock));
            }
 
            if (!OperationBlocks.Contains(operationBlock))
            {
                throw new ArgumentException(CodeAnalysisResources.InvalidOperationBlockForAnalysisContext, nameof(operationBlock));
            }
 
            return DiagnosticAnalysisContextHelpers.GetControlFlowGraph(operationBlock, _getControlFlowGraph, _cancellationToken);
        }
    }
 
    /// <summary>
    /// Context for an operation block action or operation block end action.
    /// An operation block action or operation block end action can use an <see cref="OperationAnalysisContext"/> to report <see cref="Diagnostic"/>s about an operation block.
    /// </summary>
    public readonly struct OperationBlockAnalysisContext
    {
        private readonly ImmutableArray<IOperation> _operationBlocks;
        private readonly ISymbol _owningSymbol;
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly Func<IOperation, ControlFlowGraph>? _getControlFlowGraph;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// One or more operation blocks that are the subject of the analysis.
        /// This includes all blocks associated with the <see cref="OwningSymbol"/>,
        /// such as method body, field/property/constructor/parameter initializer(s), attributes, etc.
        /// </summary>
        /// <remarks>Note that the operation blocks are not in any specific order.</remarks>
        public ImmutableArray<IOperation> OperationBlocks => _operationBlocks;
 
        /// <summary>
        /// <see cref="ISymbol"/> for which the <see cref="OperationBlocks"/> provides a definition or value.
        /// </summary>
        public ISymbol OwningSymbol => _owningSymbol;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="OperationBlocks"/>.
        /// </summary>
        public Compilation Compilation => _compilation;
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options => _options;
 
        /// <summary>
        /// Syntax tree for the <see cref="OperationBlocks"/> being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="OperationBlocks"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken => _cancellationToken;
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public OperationBlockAnalysisContext(
            ImmutableArray<IOperation> operationBlocks,
            ISymbol owningSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, bool> isSupportedDiagnostic,
            CancellationToken cancellationToken)
            : this(operationBlocks, owningSymbol, compilation, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), getControlFlowGraph: null,
                  filterTree: operationBlocks[0].Syntax.SyntaxTree, filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal OperationBlockAnalysisContext(
            ImmutableArray<IOperation> operationBlocks,
            ISymbol owningSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            Func<IOperation, ControlFlowGraph>? getControlFlowGraph,
            SyntaxTree filterTree,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _operationBlocks = operationBlocks;
            _owningSymbol = owningSymbol;
            _compilation = compilation;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            _getControlFlowGraph = getControlFlowGraph;
            FilterTree = filterTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a code block.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, Compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
 
        /// <summary>
        /// Gets a <see cref="ControlFlowGraph"/> for a given <paramref name="operationBlock"/> from this analysis context's <see cref="OperationBlocks"/>.
        /// </summary>
        /// <param name="operationBlock">Operation block.</param>
        public ControlFlowGraph GetControlFlowGraph(IOperation operationBlock)
        {
            if (operationBlock == null)
            {
                throw new ArgumentNullException(nameof(operationBlock));
            }
 
            if (!OperationBlocks.Contains(operationBlock))
            {
                throw new ArgumentException(CodeAnalysisResources.InvalidOperationBlockForAnalysisContext, nameof(operationBlock));
            }
 
            return DiagnosticAnalysisContextHelpers.GetControlFlowGraph(operationBlock, _getControlFlowGraph, _cancellationToken);
        }
    }
 
    /// <summary>
    /// Context for a syntax tree action.
    /// A syntax tree action can use a <see cref="SyntaxTreeAnalysisContext"/> to report <see cref="Diagnostic"/>s about a <see cref="SyntaxTree"/> for a code document.
    /// </summary>
    public readonly struct SyntaxTreeAnalysisContext
    {
        private readonly SyntaxTree _tree;
        private readonly Compilation? _compilationOpt;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="SyntaxTree"/> that is the subject of the analysis.
        /// </summary>
        public SyntaxTree Tree => _tree;
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options => _options;
 
        /// <summary>
        /// Optional filter span within the <see cref="Tree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="Tree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="Tree"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken => _cancellationToken;
 
        internal Compilation? Compilation => _compilationOpt;
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(tree, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), compilation: null, filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal SyntaxTreeAnalysisContext(
            SyntaxTree tree,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            Compilation? compilation,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _tree = tree;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            _compilationOpt = compilation;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a <see cref="SyntaxTree"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _compilationOpt, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for an additional file action.
    /// An additional file action can use an <see cref="AdditionalFileAnalysisContext"/> to report <see cref="Diagnostic"/>s about a non-source <see cref="AdditionalText"/> document.
    /// </summary>
    public readonly struct AdditionalFileAnalysisContext
    {
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
 
        /// <summary>
        /// <see cref="AdditionalText"/> that is the subject of the analysis.
        /// </summary>
        public AdditionalText AdditionalFile { get; }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="AdditionalFile"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="AdditionalFile"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get; }
 
        /// <summary>
        /// Compilation being analyzed.
        /// </summary>
        public Compilation Compilation { get; }
 
        internal AdditionalFileAnalysisContext(
            AdditionalText additionalFile,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            Compilation compilation,
            TextSpan? filterSpan,
            CancellationToken cancellationToken)
        {
            AdditionalFile = additionalFile;
            Options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            Compilation = compilation;
            FilterSpan = filterSpan;
            CancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a diagnostic for the given <see cref="AdditionalFile"/>.
        /// A diagnostic in a non-source document should be created with a non-source <see cref="Location"/>,
        /// which can be created using <see cref="Location.Create(string, TextSpan, LinePositionSpan)"/> API.
        /// </summary>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, Compilation, _isSupportedDiagnostic, CancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for a syntax node action.
    /// A syntax node action can use a <see cref="SyntaxNodeAnalysisContext"/> to report <see cref="Diagnostic"/>s for a <see cref="SyntaxNode"/>.
    /// </summary>
    public readonly struct SyntaxNodeAnalysisContext
    {
        private readonly SyntaxNode _node;
        private readonly ISymbol? _containingSymbol;
        private readonly SemanticModel _semanticModel;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="SyntaxNode"/> that is the subject of the analysis.
        /// </summary>
        public SyntaxNode Node => _node;
 
        /// <summary>
        /// <see cref="ISymbol"/> for the declaration containing the syntax node.
        /// </summary>
        public ISymbol? ContainingSymbol => _containingSymbol;
 
        /// <summary>
        /// <see cref="CodeAnalysis.SemanticModel"/> that can provide semantic information about the <see cref="SyntaxNode"/>.
        /// </summary>
        public SemanticModel SemanticModel => _semanticModel;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="SyntaxNode"/>.
        /// </summary>
        public Compilation Compilation => _semanticModel?.Compilation ?? throw new InvalidOperationException();
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options => _options;
 
        /// <summary>
        /// Syntax tree for the <see cref="Node"/> being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="Node"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken => _cancellationToken;
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SyntaxNodeAnalysisContext(SyntaxNode node, ISymbol? containingSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
            : this(node, containingSymbol, semanticModel, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, AnalyzerOptions options, Action<Diagnostic> reportDiagnostic, Func<Diagnostic, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
           : this(node, null, semanticModel, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal SyntaxNodeAnalysisContext(
            SyntaxNode node,
            ISymbol? containingSymbol,
            SemanticModel semanticModel,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _node = node;
            _containingSymbol = containingSymbol;
            _semanticModel = semanticModel;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            FilterTree = node.SyntaxTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a <see cref="SyntaxNode"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _semanticModel.Compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
    }
 
    /// <summary>
    /// Context for an operation action.
    /// An operation action can use an <see cref="OperationAnalysisContext"/> to report <see cref="Diagnostic"/>s for an <see cref="IOperation"/>.
    /// </summary>
    public readonly struct OperationAnalysisContext
    {
        private readonly IOperation _operation;
        private readonly ISymbol _containingSymbol;
        private readonly Compilation _compilation;
        private readonly AnalyzerOptions _options;
        private readonly Action<Diagnostic> _reportDiagnostic;
        private readonly Func<Diagnostic, CancellationToken, bool> _isSupportedDiagnostic;
        private readonly Func<IOperation, ControlFlowGraph>? _getControlFlowGraph;
        private readonly CancellationToken _cancellationToken;
 
        /// <summary>
        /// <see cref="IOperation"/> that is the subject of the analysis.
        /// </summary>
        public IOperation Operation => _operation;
 
        /// <summary>
        /// <see cref="ISymbol"/> for the declaration containing the operation.
        /// </summary>
        public ISymbol ContainingSymbol => _containingSymbol;
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> containing the <see cref="IOperation"/>.
        /// </summary>
        public Compilation Compilation => _compilation;
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options => _options;
 
        /// <summary>
        /// Syntax tree for the <see cref="Operation"/> being analyzed.
        /// </summary>
        public SyntaxTree FilterTree { get; }
 
        /// <summary>
        /// Optional filter span within the <see cref="FilterTree"/> for which to compute diagnostics.
        /// <see langword="null"/> if we are analyzing the entire <see cref="FilterTree"/>
        /// or the entire compilation.
        /// </summary>
        public TextSpan? FilterSpan { get; }
 
        /// <summary>
        /// Indicates if the <see cref="Operation"/> is generated code.
        /// </summary>
        public bool IsGeneratedCode { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken => _cancellationToken;
 
        [Obsolete("Use CompilationWithAnalyzers instead. See https://github.com/dotnet/roslyn/issues/63440 for more details.")]
        public OperationAnalysisContext(
            IOperation operation,
            ISymbol containingSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, bool> isSupportedDiagnostic,
            CancellationToken cancellationToken)
            : this(operation, containingSymbol, compilation, options, reportDiagnostic, isSupportedDiagnostic: (d, _) => isSupportedDiagnostic(d), getControlFlowGraph: null, filterSpan: null, isGeneratedCode: false, cancellationToken)
        {
        }
 
        internal OperationAnalysisContext(
            IOperation operation,
            ISymbol containingSymbol,
            Compilation compilation,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic,
            Func<IOperation, ControlFlowGraph>? getControlFlowGraph,
            TextSpan? filterSpan,
            bool isGeneratedCode,
            CancellationToken cancellationToken)
        {
            _operation = operation;
            _containingSymbol = containingSymbol;
            _compilation = compilation;
            _options = options;
            _reportDiagnostic = reportDiagnostic;
            _isSupportedDiagnostic = isSupportedDiagnostic;
            _getControlFlowGraph = getControlFlowGraph;
            FilterTree = operation.Syntax.SyntaxTree;
            FilterSpan = filterSpan;
            IsGeneratedCode = isGeneratedCode;
            _cancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Diagnostic"/> about a <see cref="SyntaxNode"/>.
        /// </summary>
        /// <param name="diagnostic"><see cref="Diagnostic"/> to be reported.</param>
        public void ReportDiagnostic(Diagnostic diagnostic)
        {
            DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _compilation, _isSupportedDiagnostic, _cancellationToken);
            lock (_reportDiagnostic)
            {
                _reportDiagnostic(diagnostic);
            }
        }
 
        /// <summary>
        /// Gets a <see cref="ControlFlowGraph"/> for the operation block containing the <see cref="Operation"/>.
        /// </summary>
        public ControlFlowGraph GetControlFlowGraph() => DiagnosticAnalysisContextHelpers.GetControlFlowGraph(Operation, _getControlFlowGraph, _cancellationToken);
    }
 
    /// <summary>
    /// Context for suppressing analyzer and/or compiler non-error diagnostics reported for the compilation.
    /// </summary>
    public readonly struct SuppressionAnalysisContext
    {
        private readonly Action<Suppression> _addSuppression;
        private readonly Func<SuppressionDescriptor, bool> _isSupportedSuppressionDescriptor;
        private readonly Func<SyntaxTree, SemanticModel> _getSemanticModel;
 
        /// <summary>
        /// Analyzer and/or compiler non-error diagnostics reported for the compilation.
        /// Each <see cref="DiagnosticSuppressor"/> only receives diagnostics whose IDs were declared suppressible in its <see cref="DiagnosticSuppressor.SupportedSuppressions"/>.
        /// This may be a subset of the full set of reported diagnostics, as an optimization for
        /// supporting incremental and partial analysis scenarios.
        /// A diagnostic is considered suppressible by a DiagnosticSuppressor if *all* of the following conditions are met:
        ///     1. Diagnostic is not already suppressed in source via pragma/suppress message attribute.
        ///     2. Diagnostic's <see cref="Diagnostic.DefaultSeverity"/> is not <see cref="DiagnosticSeverity.Error"/>.
        ///     3. Diagnostic is not tagged with <see cref="WellKnownDiagnosticTags.NotConfigurable"/> custom tag.
        /// </summary>
        public ImmutableArray<Diagnostic> ReportedDiagnostics { get; }
 
        /// <summary>
        /// <see cref="CodeAnalysis.Compilation"/> for the context.
        /// </summary>
        public Compilation Compilation { get; }
 
        /// <summary>
        /// Options specified for the analysis.
        /// </summary>
        public AnalyzerOptions Options { get; }
 
        /// <summary>
        /// Token to check for requested cancellation of the analysis.
        /// </summary>
        public CancellationToken CancellationToken { get; }
 
        internal SuppressionAnalysisContext(
            Compilation compilation,
            AnalyzerOptions options,
            ImmutableArray<Diagnostic> reportedDiagnostics,
            Action<Suppression> suppressDiagnostic,
            Func<SuppressionDescriptor, bool> isSupportedSuppressionDescriptor,
            Func<SyntaxTree, SemanticModel> getSemanticModel,
            CancellationToken cancellationToken)
        {
            Compilation = compilation;
            Options = options;
            ReportedDiagnostics = reportedDiagnostics;
            _addSuppression = suppressDiagnostic;
            _isSupportedSuppressionDescriptor = isSupportedSuppressionDescriptor;
            _getSemanticModel = getSemanticModel;
            CancellationToken = cancellationToken;
        }
 
        /// <summary>
        /// Report a <see cref="Suppression"/> for a reported diagnostic.
        /// </summary>
        public void ReportSuppression(Suppression suppression)
        {
            if (!ReportedDiagnostics.Contains(suppression.SuppressedDiagnostic))
            {
                // Non-reported diagnostic with ID '{0}' cannot be suppressed.
                var message = string.Format(CodeAnalysisResources.NonReportedDiagnosticCannotBeSuppressed, suppression.SuppressedDiagnostic.Id);
                throw new ArgumentException(message);
            }
 
            if (!_isSupportedSuppressionDescriptor(suppression.Descriptor))
            {
                // Reported suppression with ID '{0}' is not supported by the suppressor.
                var message = string.Format(CodeAnalysisResources.UnsupportedSuppressionReported, suppression.Descriptor.Id);
                throw new ArgumentException(message);
            }
 
            if (suppression.Descriptor.IsDisabled(Compilation.Options))
            {
                // Suppression has been disabled by the end user through compilation options.
                return;
            }
 
            _addSuppression(suppression);
        }
 
        /// <summary>
        /// Gets a <see cref="SemanticModel"/> for the given <see cref="SyntaxTree"/>, which is shared across all analyzers.
        /// </summary>
        public SemanticModel GetSemanticModel(SyntaxTree syntaxTree) => _getSemanticModel(syntaxTree);
    }
}