File: DiagnosticAnalyzer\AnalyzerDriver.GroupedAnalyzerActionsForAnalyzer.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.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal partial class AnalyzerDriver<TLanguageKindEnum> : AnalyzerDriver where TLanguageKindEnum : struct
    {
        private sealed class GroupedAnalyzerActionsForAnalyzer
        {
            private readonly DiagnosticAnalyzer _analyzer;
            private readonly bool _analyzerActionsNeedFiltering;
 
            private ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> _lazyNodeActionsByKind;
            private ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> _lazyOperationActionsByKind;
            private ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>> _lazyCodeBlockStartActions;
            private ImmutableArray<CodeBlockAnalyzerAction> _lazyCodeBlockEndActions;
            private ImmutableArray<CodeBlockAnalyzerAction> _lazyCodeBlockActions;
            private ImmutableArray<OperationBlockStartAnalyzerAction> _lazyOperationBlockStartActions;
            private ImmutableArray<OperationBlockAnalyzerAction> _lazyOperationBlockActions;
            private ImmutableArray<OperationBlockAnalyzerAction> _lazyOperationBlockEndActions;
 
            public GroupedAnalyzerActionsForAnalyzer(DiagnosticAnalyzer analyzer, in AnalyzerActions analyzerActions, bool analyzerActionsNeedFiltering)
            {
                Debug.Assert(!analyzerActions.IsEmpty);
 
                _analyzer = analyzer;
                AnalyzerActions = analyzerActions;
                _analyzerActionsNeedFiltering = analyzerActionsNeedFiltering;
            }
 
            public AnalyzerActions AnalyzerActions { get; }
 
            [Conditional("DEBUG")]
            private static void VerifyActions<TAnalyzerAction>(in ImmutableArray<TAnalyzerAction> actions, DiagnosticAnalyzer analyzer)
                where TAnalyzerAction : AnalyzerAction
            {
                foreach (var action in actions)
                {
                    Debug.Assert(action.Analyzer == analyzer);
                }
            }
 
            private ImmutableArray<TAnalyzerAction> GetFilteredActions<TAnalyzerAction>(in ImmutableArray<TAnalyzerAction> actions)
                where TAnalyzerAction : AnalyzerAction
                => GetFilteredActions(actions, _analyzer, _analyzerActionsNeedFiltering);
 
            private static ImmutableArray<TAnalyzerAction> GetFilteredActions<TAnalyzerAction>(
                in ImmutableArray<TAnalyzerAction> actions,
                DiagnosticAnalyzer analyzer,
                bool analyzerActionsNeedFiltering)
                where TAnalyzerAction : AnalyzerAction
            {
                if (!analyzerActionsNeedFiltering)
                {
                    return actions;
                }
 
                return actions.WhereAsArray((action, analyzer) => action.Analyzer == analyzer, analyzer);
            }
 
            public ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> NodeActionsByAnalyzerAndKind
            {
                get
                {
                    if (_lazyNodeActionsByKind == null)
                    {
                        var nodeActions = _analyzerActionsNeedFiltering ?
                            AnalyzerActions.GetSyntaxNodeActions<TLanguageKindEnum>(_analyzer) :
                            AnalyzerActions.GetSyntaxNodeActions<TLanguageKindEnum>();
                        VerifyActions(nodeActions, _analyzer);
                        var analyzerActionsByKind = !nodeActions.IsEmpty ?
                            AnalyzerExecutor.GetNodeActionsByKind(nodeActions) :
                            ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>.Empty;
                        RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyNodeActionsByKind, analyzerActionsByKind);
                    }
 
                    return _lazyNodeActionsByKind;
                }
            }
 
            public ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> OperationActionsByAnalyzerAndKind
            {
                get
                {
                    if (_lazyOperationActionsByKind == null)
                    {
                        var operationActions = GetFilteredActions(AnalyzerActions.OperationActions);
                        VerifyActions(operationActions, _analyzer);
                        var analyzerActionsByKind = operationActions.Any() ?
                            AnalyzerExecutor.GetOperationActionsByKind(operationActions) :
                            ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>.Empty;
                        RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyOperationActionsByKind, analyzerActionsByKind);
                    }
 
                    return _lazyOperationActionsByKind;
                }
            }
 
            private ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>> CodeBlockStartActions
            {
                get
                {
                    if (_lazyCodeBlockStartActions.IsDefault)
                    {
                        var codeBlockActions = GetFilteredActions(AnalyzerActions.GetCodeBlockStartActions<TLanguageKindEnum>());
                        VerifyActions(codeBlockActions, _analyzer);
                        ImmutableInterlocked.InterlockedInitialize(ref _lazyCodeBlockStartActions, codeBlockActions);
                    }
 
                    return _lazyCodeBlockStartActions;
                }
            }
 
            private ImmutableArray<CodeBlockAnalyzerAction> CodeBlockEndActions
                => GetExecutableCodeActions(ref _lazyCodeBlockEndActions, AnalyzerActions.CodeBlockEndActions, _analyzer, _analyzerActionsNeedFiltering);
 
            private ImmutableArray<CodeBlockAnalyzerAction> CodeBlockActions
                => GetExecutableCodeActions(ref _lazyCodeBlockActions, AnalyzerActions.CodeBlockActions, _analyzer, _analyzerActionsNeedFiltering);
 
            private ImmutableArray<OperationBlockStartAnalyzerAction> OperationBlockStartActions
                => GetExecutableCodeActions(ref _lazyOperationBlockStartActions, AnalyzerActions.OperationBlockStartActions, _analyzer, _analyzerActionsNeedFiltering);
 
            private ImmutableArray<OperationBlockAnalyzerAction> OperationBlockEndActions
                => GetExecutableCodeActions(ref _lazyOperationBlockEndActions, AnalyzerActions.OperationBlockEndActions, _analyzer, _analyzerActionsNeedFiltering);
 
            private ImmutableArray<OperationBlockAnalyzerAction> OperationBlockActions
                => GetExecutableCodeActions(ref _lazyOperationBlockActions, AnalyzerActions.OperationBlockActions, _analyzer, _analyzerActionsNeedFiltering);
 
            public bool HasCodeBlockStartActions => !CodeBlockStartActions.IsEmpty;
            public bool HasOperationBlockStartActions => !OperationBlockStartActions.IsEmpty;
 
            private static ImmutableArray<ActionType> GetExecutableCodeActions<ActionType>(
                ref ImmutableArray<ActionType> lazyCodeBlockActions,
                ImmutableArray<ActionType> codeBlockActions,
                DiagnosticAnalyzer analyzer,
                bool analyzerActionsNeedFiltering)
                where ActionType : AnalyzerAction
            {
                if (lazyCodeBlockActions.IsDefault)
                {
                    codeBlockActions = GetFilteredActions(codeBlockActions, analyzer, analyzerActionsNeedFiltering);
                    VerifyActions(codeBlockActions, analyzer);
                    ImmutableInterlocked.InterlockedInitialize(ref lazyCodeBlockActions, codeBlockActions);
                }
 
                return lazyCodeBlockActions;
            }
 
            public bool TryGetExecutableCodeBlockActions(out ExecutableCodeBlockAnalyzerActions actions)
            {
                if (!OperationBlockStartActions.IsEmpty ||
                    !OperationBlockActions.IsEmpty ||
                    !OperationBlockEndActions.IsEmpty ||
                    !CodeBlockStartActions.IsEmpty ||
                    !CodeBlockActions.IsEmpty ||
                    !CodeBlockEndActions.IsEmpty)
                {
                    actions = new ExecutableCodeBlockAnalyzerActions
                    {
                        Analyzer = _analyzer,
                        CodeBlockStartActions = CodeBlockStartActions,
                        CodeBlockActions = CodeBlockActions,
                        CodeBlockEndActions = CodeBlockEndActions,
                        OperationBlockStartActions = OperationBlockStartActions,
                        OperationBlockActions = OperationBlockActions,
                        OperationBlockEndActions = OperationBlockEndActions
                    };
 
                    return true;
                }
 
                actions = default;
                return false;
            }
        }
    }
}