File: Microsoft.CodeQuality.Analyzers\QualityGuidelines\AvoidMultipleEnumerations\FlowAnalysis\GlobalFlowStateDictionaryAnalysis.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis;

namespace Microsoft.CodeQuality.Analyzers.QualityGuidelines.AvoidMultipleEnumerations.FlowAnalysis
{
    using GlobalFlowStateDictionaryAnalysisData = DictionaryAnalysisData<AnalysisEntity, GlobalFlowStateDictionaryAnalysisValue>;
    using GlobalFlowStateDictionaryAnalysisResult = DataFlowAnalysisResult<GlobalFlowStateDictionaryBlockAnalysisResult, GlobalFlowStateDictionaryAnalysisValue>;
    using GlobalFlowStateDictionaryAnalysisDomain = MapAbstractDomain<AnalysisEntity, GlobalFlowStateDictionaryAnalysisValue>;

    /// <summary>
    /// An analysis that tracks the state of a set of <see cref="AnalysisEntity"/>. The state is shared among the block.
    /// </summary>
    internal class GlobalFlowStateDictionaryAnalysis : ForwardDataFlowAnalysis<
        GlobalFlowStateDictionaryAnalysisData,
        GlobalFlowStateDictionaryAnalysisContext,
        GlobalFlowStateDictionaryAnalysisResult,
        GlobalFlowStateDictionaryBlockAnalysisResult,
        GlobalFlowStateDictionaryAnalysisValue>
    {
        public static readonly GlobalFlowStateDictionaryAnalysisDomain Domain = new(GlobalFlowStateDictionaryAnalysisValueDomain.Instance);

        public GlobalFlowStateDictionaryAnalysis(
            AbstractAnalysisDomain<GlobalFlowStateDictionaryAnalysisData> analysisDomain,
            DataFlowOperationVisitor<GlobalFlowStateDictionaryAnalysisData, GlobalFlowStateDictionaryAnalysisContext, GlobalFlowStateDictionaryAnalysisResult, GlobalFlowStateDictionaryAnalysisValue> operationVisitor)
            : base(analysisDomain, operationVisitor)
        {
        }

        public static GlobalFlowStateDictionaryAnalysisResult? TryGetOrComputeResult(
            ControlFlowGraph cfg,
            ISymbol owningSymbol,
            Func<GlobalFlowStateDictionaryAnalysisContext, GlobalFlowStateDictionaryFlowOperationVisitor> createOperationVisitor,
            WellKnownTypeProvider wellKnownTypeProvider,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            bool pessimisticAnalysis,
            InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.None,
            InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null)
        {
            if (cfg == null)
            {
                throw new ArgumentNullException(nameof(cfg));
            }

            var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                analyzerOptions,
                rule,
                cfg,
                wellKnownTypeProvider.Compilation,
                interproceduralAnalysisKind);

            var pointsToAnalysisResult = PointsToAnalysis.TryGetOrComputeResult(
                cfg,
                owningSymbol,
                analyzerOptions,
                wellKnownTypeProvider,
                PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties,
                interproceduralAnalysisConfig,
                interproceduralAnalysisPredicate,
                pessimisticAnalysis: pessimisticAnalysis);

            var analysisContext = new GlobalFlowStateDictionaryAnalysisContext(
                GlobalFlowStateDictionaryAnalysisValueDomain.Instance,
                wellKnownTypeProvider,
                cfg,
                owningSymbol,
                analyzerOptions,
                interproceduralAnalysisConfig,
                pessimisticAnalysis: pessimisticAnalysis,
                predicateAnalysis: false,
                exceptionPathsAnalysis: false,
                copyAnalysisResult: null,
                pointsToAnalysisResult: pointsToAnalysisResult,
                valueContentAnalysisResult: null,
                tryGetOrComputeAnalysisResult: c => TryGetOrComputeAnalysisResult(c, createOperationVisitor),
                interproceduralAnalysisPredicate: interproceduralAnalysisPredicate);

            return TryGetOrComputeAnalysisResult(analysisContext, createOperationVisitor);
        }

        private static GlobalFlowStateDictionaryAnalysisResult? TryGetOrComputeAnalysisResult(
            GlobalFlowStateDictionaryAnalysisContext analysisContext,
            Func<GlobalFlowStateDictionaryAnalysisContext, GlobalFlowStateDictionaryFlowOperationVisitor> createOperationVisitor)
        {
            var operationVisitor = createOperationVisitor(analysisContext);
            var analysis = new GlobalFlowStateDictionaryAnalysis(Domain, operationVisitor);
            return analysis.TryGetOrComputeResultCore(analysisContext, cacheResult: false);
        }

        protected override GlobalFlowStateDictionaryAnalysisResult ToResult(GlobalFlowStateDictionaryAnalysisContext analysisContext, GlobalFlowStateDictionaryAnalysisResult dataFlowAnalysisResult)
        {
            // Use the global values map. Drop the per-operation information
            var operationVisitor = (GlobalFlowStateDictionaryFlowOperationVisitor)OperationVisitor;
            return dataFlowAnalysisResult.With(operationVisitor.GetGlobalValuesMap());
        }

        protected override GlobalFlowStateDictionaryBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, GlobalFlowStateDictionaryAnalysisData blockAnalysisData)
            => new(basicBlock, blockAnalysisData);
    }
}