File: src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis\Framework\DataFlow\AbstractDataFlowAnalysisContext.cs
Web Access
Project: src\src\RoslynAnalyzers\Microsoft.CodeAnalysis.AnalyzerUtilities\Microsoft.CodeAnalysis.AnalyzerUtilities.csproj (Microsoft.CodeAnalysis.AnalyzerUtilities)
// 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.Diagnostics;
using System.Linq;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
 
namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow
{
    using CopyAnalysisResult = DataFlowAnalysisResult<CopyBlockAnalysisResult, CopyAbstractValue>;
    using ValueContentAnalysisResult = DataFlowAnalysisResult<ValueContentBlockAnalysisResult, ValueContentAbstractValue>;
 
    /// <summary>
    /// Base type for analysis contexts for execution of <see cref="DataFlowAnalysis"/> on a control flow graph.
    /// </summary>
    public abstract class AbstractDataFlowAnalysisContext<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>
        : CacheBasedEquatable<TAnalysisContext>, IDataFlowAnalysisContext
        where TAnalysisContext : class, IDataFlowAnalysisContext
        where TAnalysisResult : class, IDataFlowAnalysisResult<TAbstractAnalysisValue>
        where TAnalysisData : AbstractAnalysisData
    {
        protected AbstractDataFlowAnalysisContext(
            AbstractValueDomain<TAbstractAnalysisValue> valueDomain,
            WellKnownTypeProvider wellKnownTypeProvider,
            ControlFlowGraph controlFlowGraph,
            ISymbol owningSymbol,
            AnalyzerOptions analyzerOptions,
            InterproceduralAnalysisConfiguration interproceduralAnalysisConfig,
            bool pessimisticAnalysis,
            bool predicateAnalysis,
            bool exceptionPathsAnalysis,
            CopyAnalysisResult? copyAnalysisResult,
            PointsToAnalysisResult? pointsToAnalysisResult,
            ValueContentAnalysisResult? valueContentAnalysisResult,
            Func<TAnalysisContext, TAnalysisResult?> tryGetOrComputeAnalysisResult,
            ControlFlowGraph? parentControlFlowGraph,
            InterproceduralAnalysisData<TAnalysisData, TAnalysisContext, TAbstractAnalysisValue>? interproceduralAnalysisData,
            InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate)
        {
            Debug.Assert(owningSymbol.Kind is SymbolKind.Method or
                SymbolKind.Field or
                SymbolKind.Property or
                SymbolKind.Event);
            Debug.Assert(Equals(owningSymbol.OriginalDefinition, owningSymbol));
            Debug.Assert(pointsToAnalysisResult == null ||
                pointsToAnalysisResult.ControlFlowGraph == controlFlowGraph);
            Debug.Assert(copyAnalysisResult == null ||
                copyAnalysisResult.ControlFlowGraph == controlFlowGraph);
            Debug.Assert(valueContentAnalysisResult == null ||
                valueContentAnalysisResult.ControlFlowGraph == controlFlowGraph);
 
            ValueDomain = valueDomain;
            WellKnownTypeProvider = wellKnownTypeProvider;
            ControlFlowGraph = controlFlowGraph;
            ParentControlFlowGraph = parentControlFlowGraph;
            OwningSymbol = owningSymbol;
            AnalyzerOptions = analyzerOptions;
            InterproceduralAnalysisConfiguration = interproceduralAnalysisConfig;
            PessimisticAnalysis = pessimisticAnalysis;
            PredicateAnalysis = predicateAnalysis;
            ExceptionPathsAnalysis = exceptionPathsAnalysis;
            CopyAnalysisResult = copyAnalysisResult;
            PointsToAnalysisResult = pointsToAnalysisResult;
            ValueContentAnalysisResult = valueContentAnalysisResult;
            TryGetOrComputeAnalysisResult = tryGetOrComputeAnalysisResult;
            InterproceduralAnalysisData = interproceduralAnalysisData;
            InterproceduralAnalysisPredicate = interproceduralAnalysisPredicate;
        }
 
        public AbstractValueDomain<TAbstractAnalysisValue> ValueDomain { get; }
        public WellKnownTypeProvider WellKnownTypeProvider { get; }
        public ControlFlowGraph ControlFlowGraph { get; }
        public ISymbol OwningSymbol { get; }
        public AnalyzerOptions AnalyzerOptions { get; }
        public InterproceduralAnalysisConfiguration InterproceduralAnalysisConfiguration { get; }
        public bool PessimisticAnalysis { get; }
        public bool PredicateAnalysis { get; }
        public bool ExceptionPathsAnalysis { get; }
        public CopyAnalysisResult? CopyAnalysisResult { get; }
        public PointsToAnalysisResult? PointsToAnalysisResult { get; }
        public ValueContentAnalysisResult? ValueContentAnalysisResult { get; }
 
        public Func<TAnalysisContext, TAnalysisResult?> TryGetOrComputeAnalysisResult { get; }
        protected ControlFlowGraph? ParentControlFlowGraph { get; }
 
        // Optional data for context sensitive analysis.
        public InterproceduralAnalysisData<TAnalysisData, TAnalysisContext, TAbstractAnalysisValue>? InterproceduralAnalysisData { get; }
        public InterproceduralAnalysisPredicate? InterproceduralAnalysisPredicate { get; }
 
        public abstract TAnalysisContext ForkForInterproceduralAnalysis(
            IMethodSymbol invokedMethod,
            ControlFlowGraph invokedCfg,
            PointsToAnalysisResult? pointsToAnalysisResult,
            CopyAnalysisResult? copyAnalysisResult,
            ValueContentAnalysisResult? valueContentAnalysisResult,
            InterproceduralAnalysisData<TAnalysisData, TAnalysisContext, TAbstractAnalysisValue>? interproceduralAnalysisData);
 
        public ControlFlowGraph? GetLocalFunctionControlFlowGraph(IMethodSymbol localFunction)
        {
            if (localFunction.Equals(OwningSymbol))
            {
                return ControlFlowGraph;
            }
 
            if (ControlFlowGraph.LocalFunctions.Contains(localFunction))
            {
                return ControlFlowGraph.GetLocalFunctionControlFlowGraph(localFunction);
            }
 
            if (ParentControlFlowGraph != null && InterproceduralAnalysisData != null)
            {
                var parentAnalysisContext = InterproceduralAnalysisData.MethodsBeingAnalyzed.FirstOrDefault(context => context.ControlFlowGraph == ParentControlFlowGraph);
                return parentAnalysisContext?.GetLocalFunctionControlFlowGraph(localFunction);
            }
 
            // Unable to find control flow graph for local function.
            // This can happen for cases where local function creation and invocations are in different interprocedural call trees.
            // See unit test "DisposeObjectsBeforeLosingScopeTests.InvocationOfLocalFunctionCachedOntoField_InterproceduralAnalysis"
            // for an example.
            // Currently, we don't support interprocedural analysis of such local function invocations.
            return null;
        }
 
        public ControlFlowGraph? GetAnonymousFunctionControlFlowGraph(IFlowAnonymousFunctionOperation lambda)
        {
            // TODO: https://github.com/dotnet/roslyn-analyzers/issues/1812
            // Remove the below workaround.
            try
            {
                return ControlFlowGraph.GetAnonymousFunctionControlFlowGraph(lambda);
            }
            catch (ArgumentOutOfRangeException)
            {
                if (ParentControlFlowGraph != null && InterproceduralAnalysisData != null)
                {
                    var parentAnalysisContext = InterproceduralAnalysisData.MethodsBeingAnalyzed.FirstOrDefault(context => context.ControlFlowGraph == ParentControlFlowGraph);
                    return parentAnalysisContext?.GetAnonymousFunctionControlFlowGraph(lambda);
                }
 
                // Unable to find control flow graph for lambda.
                // This can happen for cases where lambda creation and invocations are in different interprocedural call trees.
                // See unit test "DisposeObjectsBeforeLosingScopeTests.InvocationOfLambdaCachedOntoField_InterproceduralAnalysis"
                // for an example.
                // Currently, we don't support interprocedural analysis of such lambda invocations.
                return null;
            }
        }
 
        protected abstract void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode);
 
        protected abstract bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue> obj);
 
        protected sealed override void ComputeHashCodeParts(ref RoslynHashCode hashCode)
        {
            hashCode.Add(ValueDomain.GetHashCode());
            hashCode.Add(OwningSymbol.GetHashCode());
            hashCode.Add(ControlFlowGraph.GetHashCode());
            hashCode.Add(AnalyzerOptions.GetHashCode());
            hashCode.Add(InterproceduralAnalysisConfiguration.GetHashCode());
            hashCode.Add(PessimisticAnalysis.GetHashCode());
            hashCode.Add(PredicateAnalysis.GetHashCode());
            hashCode.Add(ExceptionPathsAnalysis.GetHashCode());
            hashCode.Add(CopyAnalysisResult.GetHashCodeOrDefault());
            hashCode.Add(PointsToAnalysisResult.GetHashCodeOrDefault());
            hashCode.Add(ValueContentAnalysisResult.GetHashCodeOrDefault());
            hashCode.Add(InterproceduralAnalysisData.GetHashCodeOrDefault());
            hashCode.Add(InterproceduralAnalysisPredicate.GetHashCodeOrDefault());
            ComputeHashCodePartsSpecific(ref hashCode);
        }
 
        protected sealed override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable<TAnalysisContext> obj)
        {
            var other = (AbstractDataFlowAnalysisContext<TAnalysisData, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue>)obj;
            return ValueDomain.GetHashCode() == other.ValueDomain.GetHashCode()
                && OwningSymbol.GetHashCode() == other.OwningSymbol.GetHashCode()
                && ControlFlowGraph.GetHashCode() == other.ControlFlowGraph.GetHashCode()
                && AnalyzerOptions.GetHashCode() == other.AnalyzerOptions.GetHashCode()
                && InterproceduralAnalysisConfiguration.GetHashCode() == other.InterproceduralAnalysisConfiguration.GetHashCode()
                && PessimisticAnalysis.GetHashCode() == other.PessimisticAnalysis.GetHashCode()
                && PredicateAnalysis.GetHashCode() == other.PredicateAnalysis.GetHashCode()
                && ExceptionPathsAnalysis.GetHashCode() == other.ExceptionPathsAnalysis.GetHashCode()
                && CopyAnalysisResult.GetHashCodeOrDefault() == other.CopyAnalysisResult.GetHashCodeOrDefault()
                && PointsToAnalysisResult.GetHashCodeOrDefault() == other.PointsToAnalysisResult.GetHashCodeOrDefault()
                && ValueContentAnalysisResult.GetHashCodeOrDefault() == other.ValueContentAnalysisResult.GetHashCodeOrDefault()
                && InterproceduralAnalysisData.GetHashCodeOrDefault() == other.InterproceduralAnalysisData.GetHashCodeOrDefault()
                && InterproceduralAnalysisPredicate.GetHashCodeOrDefault() == other.InterproceduralAnalysisPredicate.GetHashCodeOrDefault()
                && ComputeEqualsByHashCodeParts(other);
        }
    }
}