File: src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis\Analysis\PointsToAnalysis\PointsToAnalysis.PointsToDataFlowOperationVisitor.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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
using Microsoft.CodeAnalysis.Operations;
 
namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis
{
    public partial class PointsToAnalysis : ForwardDataFlowAnalysis<PointsToAnalysisData, PointsToAnalysisContext, PointsToAnalysisResult, PointsToBlockAnalysisResult, PointsToAbstractValue>
    {
        /// <summary>
        /// Operation visitor to flow the PointsTo values across a given statement in a basic block.
        /// </summary>
        private sealed class PointsToDataFlowOperationVisitor :
            PredicateAnalysisEntityDataFlowOperationVisitor<PointsToAnalysisData, PointsToAnalysisContext, PointsToAnalysisResult, PointsToAbstractValue>
        {
            private readonly DefaultPointsToValueGenerator _defaultPointsToValueGenerator;
            private readonly PointsToAnalysisDomain _pointsToAnalysisDomain;
            private readonly PooledDictionary<IOperation, ImmutableHashSet<AbstractLocation>.Builder> _escapedOperationLocationsBuilder;
            private readonly PooledDictionary<IOperation, ImmutableHashSet<AbstractLocation>.Builder> _escapedReturnValueLocationsBuilder;
            private readonly PooledDictionary<AnalysisEntity, ImmutableHashSet<AbstractLocation>.Builder> _escapedEntityLocationsBuilder;
 
            public PointsToDataFlowOperationVisitor(
                TrackedEntitiesBuilder trackedEntitiesBuilder,
                DefaultPointsToValueGenerator defaultPointsToValueGenerator,
                PointsToAnalysisDomain pointsToAnalysisDomain,
                PointsToAnalysisContext analysisContext)
                : base(analysisContext)
            {
                TrackedEntitiesBuilder = trackedEntitiesBuilder;
                _defaultPointsToValueGenerator = defaultPointsToValueGenerator;
                _pointsToAnalysisDomain = pointsToAnalysisDomain;
                _escapedOperationLocationsBuilder = PooledDictionary<IOperation, ImmutableHashSet<AbstractLocation>.Builder>.GetInstance();
                _escapedReturnValueLocationsBuilder = PooledDictionary<IOperation, ImmutableHashSet<AbstractLocation>.Builder>.GetInstance();
                _escapedEntityLocationsBuilder = PooledDictionary<AnalysisEntity, ImmutableHashSet<AbstractLocation>.Builder>.GetInstance();
 
                analysisContext.InterproceduralAnalysisData?.InitialAnalysisData?.AssertValidPointsToAnalysisData();
            }
 
            internal TrackedEntitiesBuilder TrackedEntitiesBuilder { get; }
 
            public ImmutableDictionary<IOperation, ImmutableHashSet<AbstractLocation>> GetEscapedLocationsThroughOperationsMap()
                => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedOperationLocationsBuilder);
 
            public ImmutableDictionary<IOperation, ImmutableHashSet<AbstractLocation>> GetEscapedLocationsThroughReturnValuesMap()
                => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedReturnValueLocationsBuilder);
 
            public ImmutableDictionary<AnalysisEntity, ImmutableHashSet<AbstractLocation>> GetEscapedLocationsThroughEntitiesMap()
                => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedEntityLocationsBuilder);
 
            private static ImmutableDictionary<T, ImmutableHashSet<AbstractLocation>> GetEscapedAbstractLocationsMapAndFreeBuilder<T>(
                PooledDictionary<T, ImmutableHashSet<AbstractLocation>.Builder> escapedLocationsBuilder)
                where T : class
            {
                try
                {
                    if (escapedLocationsBuilder.Count == 0)
                    {
                        return ImmutableDictionary<T, ImmutableHashSet<AbstractLocation>>.Empty;
                    }
 
                    var builder = ImmutableDictionary.CreateBuilder<T, ImmutableHashSet<AbstractLocation>>();
                    foreach ((var key, var valueBuilder) in escapedLocationsBuilder)
                    {
                        builder.Add(key, valueBuilder.ToImmutable());
                    }
 
                    return builder.ToImmutable();
                }
                finally
                {
                    escapedLocationsBuilder.Dispose();
                }
            }
 
            private bool ShouldBeTracked(AnalysisEntity analysisEntity)
                => PointsToAnalysis.ShouldBeTracked(analysisEntity, DataFlowAnalysisContext.PointsToAnalysisKind, IsDisposable);
            private PointsToAbstractValue GetValueForEntityThatShouldNotBeTracked(AnalysisEntity analysisEntity)
            {
                Debug.Assert(!ShouldBeTracked(analysisEntity));
                Debug.Assert(!CurrentAnalysisData.TryGetValue(analysisEntity, out var existingValue) || existingValue == PointsToAbstractValue.NoLocation);
 
                return !PointsToAnalysis.ShouldBeTracked(analysisEntity.Type, IsDisposable) ?
                    PointsToAbstractValue.NoLocation :
                    _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity);
            }
 
            public override PointsToAnalysisData Flow(IOperation statement, BasicBlock block, PointsToAnalysisData input)
            {
                AssertValidPointsToAnalysisData(input);
#if DEBUG
                if (block.Kind == BasicBlockKind.Exit)
                {
                    // No flow capture entities should be alive at the end of flow graph.
                    input.AssertNoFlowCaptureEntitiesTracked();
                }
#endif
 
                // Ensure PointsTo value is set for the "this" or "Me" instance.
                if (!HasAbstractValue(AnalysisEntityFactory.ThisOrMeInstance) &&
                    ShouldBeTracked(AnalysisEntityFactory.ThisOrMeInstance))
                {
                    input.SetAbstractValue(AnalysisEntityFactory.ThisOrMeInstance, ThisOrMePointsToAbstractValue);
                }
 
                var output = base.Flow(statement, block, input);
                AssertValidPointsToAnalysisData(output);
                return output;
            }
 
            public override (PointsToAnalysisData output, bool isFeasibleBranch) FlowBranch(BasicBlock fromBlock, BranchWithInfo branch, PointsToAnalysisData input)
            {
                AssertValidPointsToAnalysisData(input);
                (PointsToAnalysisData output, bool isFeasibleBranch) result = base.FlowBranch(fromBlock, branch, input);
                AssertValidPointsToAnalysisData(result.output);
                return result;
            }
 
            protected override void AddTrackedEntities(PointsToAnalysisData analysisData, HashSet<AnalysisEntity> builder, bool forInterproceduralAnalysis)
            {
                if (!analysisData.HasAnyAbstractValue &&
                    (forInterproceduralAnalysis || !_defaultPointsToValueGenerator.HasAnyTrackedEntity))
                {
                    return;
                }
 
                foreach (var entity in TrackedEntitiesBuilder.EnumerateEntities())
                {
                    if (analysisData.HasAbstractValue(entity) ||
                        !forInterproceduralAnalysis && _defaultPointsToValueGenerator.IsTrackedEntity(entity))
                    {
                        builder.Add(entity);
                    }
                }
            }
            internal override bool IsPointsToAnalysis => true;
 
            protected override bool HasAbstractValue(AnalysisEntity analysisEntity) => CurrentAnalysisData.HasAbstractValue(analysisEntity);
 
            protected override void StopTrackingEntity(AnalysisEntity analysisEntity, PointsToAnalysisData analysisData)
                => analysisData.RemoveEntries(analysisEntity);
 
            protected override PointsToAbstractValue GetAbstractValue(AnalysisEntity analysisEntity)
            {
                if (!ShouldBeTracked(analysisEntity))
                {
                    return GetValueForEntityThatShouldNotBeTracked(analysisEntity);
                }
 
                if (!CurrentAnalysisData.TryGetValue(analysisEntity, out var value))
                {
                    value = _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity);
                }
 
                return value;
            }
 
            protected override PointsToAbstractValue GetPointsToAbstractValue(IOperation operation) => base.GetCachedAbstractValue(operation);
 
            protected override PointsToAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => !PointsToAnalysis.ShouldBeTracked(type, IsDisposable) ? PointsToAbstractValue.NoLocation : PointsToAbstractValue.NullLocation;
 
            protected override bool HasAnyAbstractValue(PointsToAnalysisData data) => data.HasAnyAbstractValue;
 
            protected override void SetAbstractValue(AnalysisEntity analysisEntity, PointsToAbstractValue value)
            {
                Debug.Assert(ShouldBeTracked(analysisEntity) || !HasAbstractValue(analysisEntity));
 
                if (ShouldBeTracked(analysisEntity))
                {
                    if (value.Kind == PointsToAbstractValueKind.Undefined)
                    {
                        Debug.Assert(value == _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity));
                        Debug.Assert(!CurrentAnalysisData.TryGetValue(analysisEntity, out var currentValue) ||
                                     currentValue.Kind == PointsToAbstractValueKind.Unknown &&
                                     analysisEntity.Symbol is IParameterSymbol { RefKind: RefKind.Out });
                        return;
                    }
 
                    SetAbstractValueCore(CurrentAnalysisData, analysisEntity, value);
                    TrackedEntitiesBuilder.AddEntityAndPointsToValue(analysisEntity, value);
                }
            }
 
            protected override CopyAbstractValue GetCopyAbstractValue(IOperation operation)
            {
                if (DataFlowAnalysisContext.CopyAnalysisResult == null &&
                    AnalysisEntityFactory.TryCreate(operation, out var entity) &&
                    entity.CaptureId.HasValue &&
                    AnalysisEntityFactory.TryGetCopyValueForFlowCapture(entity.CaptureId.Value.Id, out var copyValue) &&
                    copyValue.Kind == CopyAbstractValueKind.KnownReferenceCopy)
                {
                    return copyValue;
                }
 
                return base.GetCopyAbstractValue(operation);
            }
 
            private static void SetAbstractValueCore(PointsToAnalysisData pointsToAnalysisData, AnalysisEntity analysisEntity, PointsToAbstractValue value)
                => pointsToAnalysisData.SetAbstractValue(analysisEntity, value);
 
            protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, PointsToAbstractValue assignedValue)
            {
                if (assignedValue == PointsToAbstractValue.Undefined)
                {
                    return;
                }
 
                base.SetAbstractValueForTupleElementAssignment(tupleElementEntity, assignedValueOperation, assignedValue);
            }
 
            protected override void ResetAbstractValue(AnalysisEntity analysisEntity)
            {
                if (analysisEntity.IsLValueFlowCaptureEntity)
                {
                    // Flow captures can never be re-assigned.
                    return;
                }
 
                SetAbstractValue(analysisEntity, PointsToAbstractValue.Unknown);
            }
 
            private static void ResetAbstractValueIfTracked(AnalysisEntity analysisEntity, PointsToAnalysisData pointsToAnalysisData)
            {
                if (pointsToAnalysisData.TryGetValue(analysisEntity, out var currentValue))
                {
                    pointsToAnalysisData.SetAbstractValue(analysisEntity, GetResetValue(analysisEntity, currentValue));
                }
            }
 
            // Create a dummy PointsTo value for each reference type parameter.
            protected override PointsToAbstractValue GetDefaultValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity)
                => PointsToAnalysis.ShouldBeTracked(parameter.Type, IsDisposable) ?
                    PointsToAbstractValue.Create(
                        AbstractLocation.CreateSymbolLocation(parameter, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack),
                        mayBeNull: true) :
                    PointsToAbstractValue.NoLocation;
 
            protected override void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity)
            {
                // Mark PointsTo values for ref/out parameters in non-interprocedural context as escaped.
                if (parameter.RefKind is RefKind.Ref or RefKind.Out)
                {
                    Debug.Assert(DataFlowAnalysisContext.InterproceduralAnalysisData == null);
                    var pointsToValue = GetAbstractValue(analysisEntity);
                    HandleEscapingLocations(analysisEntity, _escapedEntityLocationsBuilder, analysisEntity, pointsToValue);
                }
            }
 
            private static PointsToAbstractValue GetResetValue(AnalysisEntity analysisEntity, PointsToAbstractValue currentValue)
            {
                if (analysisEntity.IsLValueFlowCaptureEntity)
                {
                    // LValue flow capture PointsToAbstractValue can never change.
                    return currentValue;
                }
 
                return PointsToAbstractValue.Unknown;
            }
 
            protected override void ResetCurrentAnalysisData() => CurrentAnalysisData.Reset(GetResetValue);
 
            protected override PointsToAbstractValue ComputeAnalysisValueForReferenceOperation(IOperation operation, PointsToAbstractValue defaultValue)
            {
                if (PointsToAnalysis.ShouldBeTracked(operation.Type, IsDisposable) &&
                    AnalysisEntityFactory.TryCreate(operation, out var analysisEntity))
                {
                    return GetAbstractValue(analysisEntity);
                }
                else
                {
                    Debug.Assert(operation.Type == null || !operation.Type.IsNonNullableValueType() || defaultValue == PointsToAbstractValue.NoLocation);
                    return defaultValue;
                }
            }
 
            protected override PointsToAbstractValue ComputeAnalysisValueForEscapedRefOrOutArgument(AnalysisEntity analysisEntity, IArgumentOperation operation, PointsToAbstractValue defaultValue)
            {
                Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out);
 
                if (!ShouldBeTracked(analysisEntity))
                {
                    return GetValueForEntityThatShouldNotBeTracked(analysisEntity);
                }
 
                var location = AbstractLocation.CreateAllocationLocation(operation, analysisEntity.Type, DataFlowAnalysisContext);
                return PointsToAbstractValue.Create(location, mayBeNull: true);
            }
 
            protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped)
            {
                base.PostProcessArgument(operation, isEscaped);
 
                if (!isEscaped)
                {
                    // Update abstract value for unescaped ref or out argument (interprocedural analysis case).
                    if ((operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out) &&
                        AnalysisEntityFactory.TryCreate(operation, out var analysisEntity))
                    {
                        CacheAbstractValue(operation, GetAbstractValue(analysisEntity));
 
                        if (analysisEntity.Symbol?.Kind == SymbolKind.Field)
                        {
                            // Ref/Out field argument is considered escaped.
                            HandleEscapingOperation(operation, operation);
                        }
                    }
                }
                else if (operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out)
                {
                    if (operation.Parameter.RefKind == RefKind.Ref)
                    {
                        // Input by-ref argument passed to invoked method is considered escaped in non-interprocedural analysis case.
                        HandleEscapingOperation(operation, operation.Value);
                    }
 
                    // Output by-ref or out argument might be escaped if assigned to a field.
                    HandlePossibleEscapingForAssignment(target: operation.Value, value: operation, operation: operation);
                }
            }
 
            protected override void ProcessReturnValue(IOperation? returnValue)
            {
                base.ProcessReturnValue(returnValue);
 
                // Escape the return value if we are not analyzing an invoked method during interprocedural analysis.
                if (returnValue != null &&
                    DataFlowAnalysisContext.InterproceduralAnalysisData == null)
                {
                    HandleEscapingOperation(escapingOperation: returnValue, escapedInstance: returnValue, _escapedReturnValueLocationsBuilder);
                }
            }
 
            private protected override PointsToAbstractValue GetAbstractValueForImplicitWrappingTaskCreation(IOperation returnValueOperation, PointsToAbstractValue returnValue, PointsToAbstractValue implicitTaskPointsToValue)
            {
                return implicitTaskPointsToValue;
            }
 
            #region Predicate analysis
            private static bool IsValidValueForPredicateAnalysis(NullAbstractValue value)
            {
                return value switch
                {
                    NullAbstractValue.Null
                    or NullAbstractValue.NotNull => true,
                    _ => false,
                };
            }
 
            protected override PredicateValueKind SetValueForEqualsOrNotEqualsComparisonOperator(
                IOperation leftOperand,
                IOperation rightOperand,
                bool equals,
                bool isReferenceEquality,
                PointsToAnalysisData targetAnalysisData)
            {
                var predicateValueKind = PredicateValueKind.Unknown;
 
                // Handle "a == null" and "a != null"
                if (SetValueForNullCompare(leftOperand, rightOperand, equals, ref predicateValueKind, targetAnalysisData))
                {
                    return predicateValueKind;
                }
 
                // Otherwise, handle "null == a" and "null != a"
                SetValueForNullCompare(rightOperand, leftOperand, equals, ref predicateValueKind, targetAnalysisData);
                return predicateValueKind;
            }
 
            protected override PredicateValueKind SetValueForIsNullComparisonOperator(IOperation leftOperand, bool equals, PointsToAnalysisData targetAnalysisData)
            {
                var predicateValueKind = PredicateValueKind.Unknown;
                SetValueForNullCompare(leftOperand, value: NullAbstractValue.Null, equals: equals, predicateValueKind: ref predicateValueKind, targetAnalysisData: targetAnalysisData);
                return predicateValueKind;
            }
 
            private bool SetValueForNullCompare(
                IOperation target,
                IOperation assignedValueOperation,
                bool equals,
                ref PredicateValueKind predicateValueKind,
                PointsToAnalysisData targetAnalysisData)
            {
                NullAbstractValue value = GetNullAbstractValue(assignedValueOperation);
                return SetValueForNullCompare(target, value, equals, ref predicateValueKind, targetAnalysisData);
            }
 
            private bool SetValueForNullCompare(
                IOperation target,
                NullAbstractValue value,
                bool equals,
                ref PredicateValueKind predicateValueKind,
                PointsToAnalysisData targetAnalysisData)
            {
                if (IsValidValueForPredicateAnalysis(value) &&
                    AnalysisEntityFactory.TryCreate(target, out var targetEntity) &&
                    ShouldBeTracked(targetEntity))
                {
                    // Comparison with a non-null value guarantees that we can infer result in only one of the branches.
                    // For example, predicate "a == c", where we know 'c' is non-null, guarantees 'a' is non-null in CurrentAnalysisData,
                    bool inferInTargetAnalysisData = !(value == NullAbstractValue.NotNull && !equals);
 
                    CopyAbstractValue copyValue = GetCopyAbstractValue(target);
                    if (copyValue.Kind.IsKnown())
                    {
                        foreach (var analysisEntity in copyValue.AnalysisEntities)
                        {
                            SetValueForNullCompareFromPredicate(analysisEntity, value, targetEntity.Type, equals, inferInTargetAnalysisData,
                                ref predicateValueKind, _defaultPointsToValueGenerator, WellKnownTypeProvider.Compilation, IsDisposable,
                                sourceAnalysisData: CurrentAnalysisData, targetAnalysisData: targetAnalysisData);
                        }
                    }
                    else
                    {
                        SetValueForNullCompareFromPredicate(targetEntity, value, targetEntity.Type, equals, inferInTargetAnalysisData,
                            ref predicateValueKind, _defaultPointsToValueGenerator, WellKnownTypeProvider.Compilation, IsDisposable,
                            sourceAnalysisData: CurrentAnalysisData, targetAnalysisData: targetAnalysisData);
                    }
 
                    return true;
                }
 
                return false;
            }
 
            private static void SetValueForNullCompareFromPredicate(
                AnalysisEntity key,
                NullAbstractValue value,
                ITypeSymbol targetType,
                bool equals,
                bool inferInTargetAnalysisData,
                ref PredicateValueKind predicateValueKind,
                DefaultPointsToValueGenerator defaultPointsToValueGenerator,
                Compilation compilation,
                Func<ITypeSymbol?, bool> isDisposable,
                PointsToAnalysisData sourceAnalysisData,
                PointsToAnalysisData targetAnalysisData)
            {
                if (!PointsToAnalysis.ShouldBeTracked(key, defaultPointsToValueGenerator.PointsToAnalysisKind, isDisposable))
                {
                    Debug.Assert(!targetAnalysisData.HasAbstractValue(key));
                    Debug.Assert(!sourceAnalysisData.HasAbstractValue(key));
                    return;
                }
 
                // Compute the negated value.
                NullAbstractValue negatedValue = NegatePredicateValue(value);
 
                // Check if the key already has an existing "Null" or "NotNull" NullState that would make the condition always true or false.
                // If so, set the predicateValueKind to always true/false, set the value in branch that can never be taken to NullAbstractValue.Invalid
                // and turn off value inference in one of the branch.
                if (sourceAnalysisData.TryGetValue(key, out var existingPointsToValue))
                {
                    NullAbstractValue existingNullValue = existingPointsToValue.NullState;
                    if (IsValidValueForPredicateAnalysis(existingNullValue) &&
                        (existingNullValue == NullAbstractValue.Null || value == NullAbstractValue.Null))
                    {
                        if (value == existingNullValue && equals ||
                            negatedValue == existingNullValue && !equals)
                        {
                            predicateValueKind = PredicateValueKind.AlwaysTrue;
                            negatedValue = NullAbstractValue.Invalid;
                            inferInTargetAnalysisData = false;
                        }
 
                        if (negatedValue == existingNullValue && equals ||
                            value == existingNullValue && !equals)
                        {
                            predicateValueKind = PredicateValueKind.AlwaysFalse;
                            value = NullAbstractValue.Invalid;
                        }
                    }
                }
 
                // Swap value and negatedValue if we are processing not-equals operator.
                if (!equals)
                {
                    if (value != NullAbstractValue.Invalid && negatedValue != NullAbstractValue.Invalid)
                    {
                        value = negatedValue;
                    }
                }
 
                if (inferInTargetAnalysisData)
                {
                    // Set value for the CurrentAnalysisData.
                    SetAbstractValueFromPredicate(key, value, targetType, defaultPointsToValueGenerator, compilation, sourceAnalysisData, targetAnalysisData);
                }
 
                return;
 
                static void SetAbstractValueFromPredicate(
                    AnalysisEntity analysisEntity,
                    NullAbstractValue nullState,
                    ITypeSymbol targetType,
                    DefaultPointsToValueGenerator defaultPointsToValueGenerator,
                    Compilation compilation,
                    PointsToAnalysisData sourceAnalysisData,
                    PointsToAnalysisData targetAnalysisData)
                {
                    AssertValidPointsToAnalysisData(sourceAnalysisData);
                    AssertValidPointsToAnalysisData(targetAnalysisData);
 
                    Debug.Assert(IsValidValueForPredicateAnalysis(nullState) || nullState == NullAbstractValue.Invalid);
 
                    // Ensure that the predicated 'value' can be flowed from the target type to the analysis entity.
                    if (!SymbolEqualityComparer.Default.Equals(targetType, analysisEntity.Type))
                    {
                        var conversion = compilation.ClassifyCommonConversion(targetType, analysisEntity.Type);
                        if (!conversion.Exists)
                        {
                            // No conversion exists, so we bail out from flowing the predicated value.
                            return;
                        }
 
                        if (!conversion.IsIdentity && !conversion.IsNumeric && !conversion.IsNullable)
                        {
                            // For 'Null' predicated value, there needs to be an explicit conversion
                            // from targetType to the analysisEntity's type to flow the predicated value.
                            // For 'NotNull' predicated value, there needs to be an implicit conversion
                            // from targetType to the analysisEntity's type to flow the predicated value.
                            if (nullState == NullAbstractValue.Null && conversion.IsImplicit ||
                                nullState == NullAbstractValue.NotNull && !conversion.IsImplicit)
                            {
                                return;
                            }
                        }
                    }
 
                    if (!sourceAnalysisData.TryGetValue(analysisEntity, out var existingValue))
                    {
                        existingValue = defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity);
                    }
 
                    var newPointsToValue = nullState switch
                    {
                        NullAbstractValue.Null => existingValue.MakeNull(),
 
                        NullAbstractValue.NotNull => existingValue.MakeNonNull(),
 
                        NullAbstractValue.Invalid => PointsToAbstractValue.Invalid,
 
                        _ => throw new InvalidProgramException(),
                    };
 
                    targetAnalysisData.SetAbstractValue(analysisEntity, newPointsToValue);
                    AssertValidPointsToAnalysisData(targetAnalysisData);
                }
            }
 
            private static NullAbstractValue NegatePredicateValue(NullAbstractValue value)
            {
                Debug.Assert(IsValidValueForPredicateAnalysis(value));
 
                return value switch
                {
                    NullAbstractValue.Null => NullAbstractValue.NotNull,
 
                    NullAbstractValue.NotNull => NullAbstractValue.Null,
 
                    _ => throw new InvalidProgramException(),
                };
            }
            #endregion
 
            protected override PointsToAnalysisData MergeAnalysisData(PointsToAnalysisData value1, PointsToAnalysisData value2)
                => _pointsToAnalysisDomain.Merge(value1, value2);
            protected override PointsToAnalysisData MergeAnalysisDataForBackEdge(PointsToAnalysisData value1, PointsToAnalysisData value2, BasicBlock forBlock)
                => _pointsToAnalysisDomain.MergeAnalysisDataForBackEdge(value1, value2, GetChildAnalysisEntities, ResetAbstractValueIfTracked, IsDisposable);
            protected override void UpdateValuesForAnalysisData(PointsToAnalysisData targetAnalysisData)
                => UpdateValuesForAnalysisData(targetAnalysisData.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData);
            protected override PointsToAnalysisData GetClonedAnalysisData(PointsToAnalysisData analysisData)
                => (PointsToAnalysisData)analysisData.Clone();
            public override PointsToAnalysisData GetEmptyAnalysisData()
                => new(IsDisposable);
            protected override PointsToAnalysisData GetExitBlockOutputData(PointsToAnalysisResult analysisResult)
                => new(analysisResult.ExitBlockOutput.Data, IsDisposable);
            protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(PointsToAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType)
                => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData, throwBranchWithExceptionType);
            protected override void AssertValidAnalysisData(PointsToAnalysisData analysisData)
                => AssertValidPointsToAnalysisData(analysisData);
            protected override bool Equals(PointsToAnalysisData value1, PointsToAnalysisData value2)
                => value1.Equals(value2);
 
            protected override void ApplyInterproceduralAnalysisResultCore(PointsToAnalysisData resultData)
            {
                ApplyInterproceduralAnalysisResultHelper(resultData.CoreAnalysisData);
                AssertValidPointsToAnalysisData(CurrentAnalysisData);
            }
 
            protected override PointsToAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable<AnalysisEntity> withEntities)
            {
                var trimmedData = GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData.CoreAnalysisData, SetAbstractValueCore);
                AssertValidPointsToAnalysisData(trimmedData);
                return trimmedData;
            }
 
            protected override PointsToAnalysisData GetInitialInterproceduralAnalysisData(
                IMethodSymbol invokedMethod,
                (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance,
                (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller,
                ImmutableDictionary<IParameterSymbol, ArgumentInfo<PointsToAbstractValue>> argumentValuesMap,
                IDictionary<AnalysisEntity, PointsToAbstractValue>? pointsToValues,
                IDictionary<AnalysisEntity, CopyAbstractValue>? copyValues,
                IDictionary<AnalysisEntity, ValueContentAbstractValue>? valueContentValues,
                bool isLambdaOrLocalFunction,
                bool hasParameterWithDelegateType)
            {
                pointsToValues = CurrentAnalysisData.CoreAnalysisData;
                var initialAnalysisData = base.GetInitialInterproceduralAnalysisData(invokedMethod,
                    invocationInstance, thisOrMeInstanceForCaller, argumentValuesMap, pointsToValues,
                    copyValues, valueContentValues, isLambdaOrLocalFunction, hasParameterWithDelegateType);
                AssertValidPointsToAnalysisData(initialAnalysisData);
                return initialAnalysisData;
            }
 
            private void HandleEscapingOperation(IOperation escapingOperation, IOperation escapedInstance)
            {
                HandleEscapingOperation(escapingOperation, escapedInstance, _escapedOperationLocationsBuilder);
            }
 
            private void HandleEscapingOperation(IOperation escapingOperation, IOperation escapedInstance, PooledDictionary<IOperation, ImmutableHashSet<AbstractLocation>.Builder> builder)
            {
                PointsToAbstractValue escapedInstancePointsToValue = GetPointsToAbstractValue(escapedInstance);
                if (escapedInstancePointsToValue.Kind == PointsToAbstractValueKind.KnownLValueCaptures)
                {
                    foreach (var capturedOperation in escapedInstancePointsToValue.LValueCapturedOperations)
                    {
                        HandleEscapingOperation(escapingOperation, capturedOperation, builder);
                    }
 
                    return;
                }
 
                AnalysisEntityFactory.TryCreate(escapedInstance, out var escapedEntityOpt);
                HandleEscapingLocations(escapingOperation, builder, escapedEntityOpt, escapedInstancePointsToValue);
            }
 
            private void HandleEscapingLocations<TKey>(
                TKey key,
                PooledDictionary<TKey, ImmutableHashSet<AbstractLocation>.Builder> escapedLocationsBuilder,
                AnalysisEntity? escapedEntity,
                PointsToAbstractValue escapedInstancePointsToValue)
                where TKey : class
            {
                // Start by clearing escaped locations from previous flow analysis iterations.
                if (escapedLocationsBuilder.TryGetValue(key, out var builder))
                {
                    builder.Clear();
                }
 
                HandleEscapingLocations(key, escapedLocationsBuilder, escapedInstancePointsToValue);
 
                // For value type entities, we also need to handle escaping the locations for child entities.
                if (escapedEntity?.Type.HasValueCopySemantics() == true)
                {
                    HandleEscapingLocations(key, escapedLocationsBuilder, escapedEntity.InstanceLocation);
                }
            }
 
            private void HandleEscapingLocations<TKey>(
                TKey key,
                PooledDictionary<TKey, ImmutableHashSet<AbstractLocation>.Builder> escapedLocationsBuilder,
                PointsToAbstractValue pointsToValueOfEscapedInstance)
                where TKey : class
            {
                if (pointsToValueOfEscapedInstance.Locations.IsEmpty ||
                    pointsToValueOfEscapedInstance == PointsToAbstractValue.NoLocation ||
                    pointsToValueOfEscapedInstance == PointsToAbstractValue.NullLocation)
                {
                    return;
                }
 
                if (!escapedLocationsBuilder.TryGetValue(key, out var builder))
                {
                    builder = ImmutableHashSet.CreateBuilder<AbstractLocation>();
                    escapedLocationsBuilder.Add(key, builder);
                }
 
                HandleEscapingLocations(pointsToValueOfEscapedInstance, builder);
                foreach (var childEntity in GetChildAnalysisEntities(pointsToValueOfEscapedInstance))
                {
                    var pointsToValueOfEscapedChild = GetAbstractValue(childEntity);
                    HandleEscapingLocations(pointsToValueOfEscapedChild, builder);
                }
 
                if (TryGetTaskWrappedValue(pointsToValueOfEscapedInstance, out var wrappedValue))
                {
                    HandleEscapingLocations(key, escapedLocationsBuilder, wrappedValue);
                }
            }
 
            private void HandleEscapingLocations(PointsToAbstractValue pointsToValueOfEscapedInstance, ImmutableHashSet<AbstractLocation>.Builder builder)
            {
                foreach (var escapedLocation in pointsToValueOfEscapedInstance.Locations)
                {
                    // Only escape locations associated with creations.
                    // We can expand this for more cases in future if need arises.
                    if (escapedLocation.Creation != null)
                    {
                        builder.Add(escapedLocation);
                    }
                }
 
                MarkEscapedLambdasAndLocalFunctions(pointsToValueOfEscapedInstance);
            }
 
            private void HandlePossibleEscapingForAssignment(IOperation target, IOperation value, IOperation operation)
            {
                // FxCop compat: The object assigned to a field or a property or an array element is considered escaped.
                // TODO: Perform better analysis for array element assignments as we already track element locations.
                // https://github.com/dotnet/roslyn-analyzers/issues/1577
                if (target is IMemberReferenceOperation ||
                    target.Kind == OperationKind.ArrayElementReference)
                {
                    HandleEscapingOperation(operation, value);
                }
            }
 
            protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, PointsToAbstractValue assignedValue, bool mayBeAssignment = false)
            {
                base.SetAbstractValueForAssignment(target, assignedValueOperation, assignedValue, mayBeAssignment);
 
                if (assignedValueOperation != null)
                {
                    HandlePossibleEscapingForAssignment(target, assignedValueOperation, assignedValueOperation);
                }
            }
 
            protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray<AbstractIndex> indices, ITypeSymbol elementType, IOperation initializer, PointsToAbstractValue value)
            {
                base.SetAbstractValueForArrayElementInitializer(arrayCreation, indices, elementType, initializer, value);
 
                // We use the array initializer as the escaping operation instead of arrayCreation
                // to ensure we have a unique escaping operation key for each initializer.
                HandleEscapingOperation(initializer, initializer);
            }
 
            #region Visitor methods
 
            public override PointsToAbstractValue DefaultVisit(IOperation operation, object? argument)
            {
                _ = base.DefaultVisit(operation, argument);
 
                // Special handling for:
                //  1. Null value: NullLocation
                //  2. Constants and value types do not point to any location.
                if (operation.ConstantValue.HasValue)
                {
                    if (operation.Type == null ||
                        operation.ConstantValue.Value == null)
                    {
                        return PointsToAbstractValue.NullLocation;
                    }
                    else
                    {
                        return PointsToAbstractValue.NoLocation;
                    }
                }
                else if (operation.Type.IsNonNullableValueType())
                {
                    return PointsToAbstractValue.NoLocation;
                }
 
                return ValueDomain.UnknownOrMayBeValue;
            }
 
            public override PointsToAbstractValue VisitIsType(IIsTypeOperation operation, object? argument)
            {
                _ = base.VisitIsType(operation, argument);
                return PointsToAbstractValue.NoLocation;
            }
 
            public override PointsToAbstractValue VisitInstanceReference(IInstanceReferenceOperation operation, object? argument)
            {
                _ = base.VisitInstanceReference(operation, argument);
                IOperation? currentInstanceOperation = operation.GetInstance(IsInsideAnonymousObjectInitializer);
                var value = currentInstanceOperation != null ?
                    GetCachedAbstractValue(currentInstanceOperation) :
                    ThisOrMePointsToAbstractValue;
                Debug.Assert(value.NullState == NullAbstractValue.NotNull || DataFlowAnalysisContext.InterproceduralAnalysisData != null);
                return value;
            }
 
            private PointsToAbstractValue VisitTypeCreationWithArgumentsAndInitializer<TOperation>(
                TOperation operation,
                object? argument,
                Func<TOperation, object?, PointsToAbstractValue?> baseVisit)
                where TOperation : IOperation
            {
                AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type!, DataFlowAnalysisContext);
                var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false);
                CacheAbstractValue(operation, pointsToAbstractValue);
 
                _ = baseVisit(operation, argument);
 
                return pointsToAbstractValue;
            }
 
            public override PointsToAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument)
            {
                return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitObjectCreation);
            }
 
            public override PointsToAbstractValue VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation, object? argument)
            {
                return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitDynamicObjectCreation);
            }
 
            public override PointsToAbstractValue VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation, object? argument)
            {
                return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitAnonymousObjectCreation);
            }
 
            public override PointsToAbstractValue VisitReDimClause(IReDimClauseOperation operation, object? argument)
            {
                if (operation.Operand.Type == null)
                {
                    return base.VisitReDimClause(operation, argument)!;
                }
 
                AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Operand.Type, DataFlowAnalysisContext);
                var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false);
                CacheAbstractValue(operation, pointsToAbstractValue);
 
                _ = base.VisitReDimClause(operation, argument);
 
                return pointsToAbstractValue;
            }
 
            public override PointsToAbstractValue VisitTuple(ITupleOperation operation, object? argument)
            {
                PointsToAbstractValue pointsToAbstractValue;
                if (operation.Type.GetUnderlyingValueTupleTypeOrThis() is { } type)
                {
                    AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, type, DataFlowAnalysisContext);
                    pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false);
                }
                else
                {
                    pointsToAbstractValue = PointsToAbstractValue.Unknown;
                }
 
                CacheAbstractValue(operation, pointsToAbstractValue);
                _ = base.VisitTuple(operation, argument);
                return pointsToAbstractValue;
            }
 
            public override PointsToAbstractValue VisitDelegateCreation(IDelegateCreationOperation operation, object? argument)
            {
                _ = base.VisitDelegateCreation(operation, argument);
                if (operation.Type is null)
                    return PointsToAbstractValue.Unknown;
 
                AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext);
                return PointsToAbstractValue.Create(location, mayBeNull: false);
            }
 
            public override PointsToAbstractValue VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation, object? argument)
            {
                return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitTypeParameterObjectCreation);
            }
 
            public override PointsToAbstractValue VisitArrayInitializer(IArrayInitializerOperation operation, object? argument)
            {
                _ = base.VisitArrayInitializer(operation, argument);
 
                // We should have created a new PointsTo value for the associated array creation operation in non-error code.
                // Bail out otherwise.
                var arrayCreation = operation.GetAncestor<IArrayCreationOperation>(OperationKind.ArrayCreation);
                return arrayCreation != null ? GetCachedAbstractValue(arrayCreation) : ValueDomain.UnknownOrMayBeValue;
            }
 
            public override PointsToAbstractValue VisitArrayCreation(IArrayCreationOperation operation, object? argument)
            {
                var pointsToAbstractValue = operation.Type != null
                    ? PointsToAbstractValue.Create(AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext), mayBeNull: false)
                    : PointsToAbstractValue.Unknown;
                CacheAbstractValue(operation, pointsToAbstractValue);
 
                _ = VisitArray(operation.DimensionSizes, argument);
                var initializerValue = Visit(operation.Initializer, argument);
                Debug.Assert(operation.Initializer == null || initializerValue == pointsToAbstractValue);
                return pointsToAbstractValue;
            }
 
            public override PointsToAbstractValue VisitInterpolatedString(IInterpolatedStringOperation operation, object? argument)
            {
                _ = base.VisitInterpolatedString(operation, argument);
                return PointsToAbstractValue.NoLocation;
            }
 
            public override PointsToAbstractValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument)
            {
                _ = base.VisitBinaryOperatorCore(operation, argument);
                return PointsToAbstractValue.Unknown;
            }
 
            public override PointsToAbstractValue VisitSizeOf(ISizeOfOperation operation, object? argument)
            {
                _ = base.VisitSizeOf(operation, argument);
                return PointsToAbstractValue.NoLocation;
            }
 
            public override PointsToAbstractValue VisitTypeOf(ITypeOfOperation operation, object? argument)
            {
                _ = base.VisitTypeOf(operation, argument);
                return PointsToAbstractValue.NoLocation;
            }
 
            private PointsToAbstractValue VisitInvocationCommon(IOperation operation, IOperation? instance)
            {
                if (PointsToAnalysis.ShouldBeTracked(operation.Type, IsDisposable))
                {
                    if (TryGetInterproceduralAnalysisResult(operation, out var interproceduralResult))
                    {
                        return interproceduralResult.ReturnValueAndPredicateKind!.Value.Value;
                    }
 
                    AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext);
                    var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: true);
                    return GetValueBasedOnInstanceOrReferenceValue(referenceOrInstance: instance, operation: operation, defaultValue: pointsToAbstractValue);
                }
                else
                {
                    return PointsToAbstractValue.NoLocation;
                }
            }
 
            public override PointsToAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction(
                IMethodSymbol method,
                IOperation? visitedInstance,
                ImmutableArray<IArgumentOperation> visitedArguments,
                bool invokedAsDelegate,
                IOperation originalOperation,
                PointsToAbstractValue defaultValue)
            {
                _ = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue);
 
                if (!visitedArguments.IsEmpty &&
                    method.IsCollectionAddMethod(CollectionNamedTypes))
                {
                    // FxCop compat: The object added to a collection is considered escaped.
                    foreach (var argument in visitedArguments)
                    {
                        HandleEscapingOperation(argument, argument.Value);
                    }
                }
 
                var value = VisitInvocationCommon(originalOperation, visitedInstance);
 
                if (IsSpecialMethodReturningNonNullValue(method, DataFlowAnalysisContext.WellKnownTypeProvider) &&
                    !TryGetInterproceduralAnalysisResult(originalOperation, out _))
                {
                    return value.MakeNonNull();
                }
 
                return value;
            }
 
            private static bool IsSpecialMethodReturningNonNullValue(IMethodSymbol method, WellKnownTypeProvider wellKnownTypeProvider)
                => IsSpecialFactoryMethodReturningNonNullValue(method, wellKnownTypeProvider) || IsSpecialEmptyMember(method);
 
            /// <summary>
            /// Returns true if this special static factory method whose name starts with "Create", such that
            /// method's return type is not nullable, i.e. 'type?', and
            /// method's containing type is static OR a special type OR derives from or is same as the type of the field/property/method return.
            /// For example: class SomeType { static SomeType CreateXXX(...); }
            /// </summary>
            private static bool IsSpecialFactoryMethodReturningNonNullValue(IMethodSymbol method, WellKnownTypeProvider wellKnownTypeProvider)
            {
                if (!method.IsStatic ||
                    !method.Name.StartsWith("Create", StringComparison.Ordinal) ||
                    method.ReturnType.NullableAnnotation == NullableAnnotation.Annotated)
                {
                    return false;
                }
 
                // 'Activator.CreateInstance' can return 'null'.
                // Even though it is nullable annotated to return 'object?',
                // the NullableAnnotation check above fails for VB, so we special case it here.
                if (method.Name.Equals("CreateInstance", StringComparison.Ordinal) &&
                    wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemActivator) is { } activatorType &&
                    activatorType.Equals(method.ContainingType))
                {
                    return false;
                }
 
                return method.ContainingType.IsStatic ||
                     method.ContainingType.SpecialType != SpecialType.None ||
                     method.ReturnType is INamedTypeSymbol namedType &&
                     method.ContainingType.DerivesFromOrImplementsAnyConstructionOf(namedType.OriginalDefinition);
            }
 
            /// <summary>
            /// Returns true if this special member symbol named "Empty", such that one of the following is true:
            ///  1. It is a static method with no parameters or
            ///  2. It is a static readonly property or
            ///  3. It is static readonly field
            /// and symbol's containing type is a special type or derives from or is same as the type of the field/property/method return.
            /// For example:
            ///  1. class SomeType { static readonly SomeType Empty; }
            ///  2. class SomeType { static readonly SomeType Empty { get; } }
            ///  3. class SomeType { static SomeType Empty(); }
            /// </summary>
            private static bool IsSpecialEmptyMember(ISymbol symbol)
            {
                return symbol.IsStatic &&
                    symbol.Name.Equals("Empty", StringComparison.Ordinal) &&
                    (symbol.IsReadOnlyFieldOrProperty() || symbol.Kind == SymbolKind.Method) &&
                    (symbol.ContainingType.SpecialType != SpecialType.None ||
                     symbol.GetMemberType() is INamedTypeSymbol namedType &&
                     symbol.ContainingType.DerivesFromOrImplementsAnyConstructionOf(namedType.OriginalDefinition));
            }
 
            public override PointsToAbstractValue VisitInvocation_LocalFunction(
                IMethodSymbol localFunction,
                ImmutableArray<IArgumentOperation> visitedArguments,
                IOperation originalOperation,
                PointsToAbstractValue defaultValue)
            {
                _ = base.VisitInvocation_LocalFunction(localFunction, visitedArguments, originalOperation, defaultValue);
                return VisitInvocationCommon(originalOperation, instance: null);
            }
 
            public override PointsToAbstractValue VisitInvocation_Lambda(
                IFlowAnonymousFunctionOperation lambda,
                ImmutableArray<IArgumentOperation> visitedArguments,
                IOperation originalOperation,
                PointsToAbstractValue defaultValue)
            {
                _ = base.VisitInvocation_Lambda(lambda, visitedArguments, originalOperation, defaultValue);
                return VisitInvocationCommon(originalOperation, instance: null);
            }
 
            public override PointsToAbstractValue VisitDynamicInvocation(IDynamicInvocationOperation operation, object? argument)
            {
                _ = base.VisitDynamicInvocation(operation, argument);
                return VisitInvocationCommon(operation, operation.Operation);
            }
 
            private NullAbstractValue GetNullStateBasedOnInstanceOrReferenceValue(IOperation? referenceOrInstance, ITypeSymbol? operationType, NullAbstractValue defaultValue)
            {
                if (operationType.IsNonNullableValueType())
                {
                    return NullAbstractValue.NotNull;
                }
 
                NullAbstractValue referenceOrInstanceValue = referenceOrInstance != null ? GetNullAbstractValue(referenceOrInstance) : NullAbstractValue.NotNull;
                return referenceOrInstanceValue switch
                {
                    NullAbstractValue.Invalid
                    or NullAbstractValue.Null => referenceOrInstanceValue,
                    _ => defaultValue,
                };
            }
 
            private PointsToAbstractValue GetValueBasedOnInstanceOrReferenceValue(IOperation? referenceOrInstance, IOperation operation, PointsToAbstractValue defaultValue)
            {
                NullAbstractValue nullState = GetNullStateBasedOnInstanceOrReferenceValue(referenceOrInstance, operation.Type, defaultValue.NullState);
                return nullState switch
                {
                    NullAbstractValue.NotNull => defaultValue.MakeNonNull(),
 
                    NullAbstractValue.Null => defaultValue.MakeNull(),
 
                    NullAbstractValue.Invalid => PointsToAbstractValue.Invalid,
 
                    _ => defaultValue,
                };
            }
 
            public override PointsToAbstractValue VisitFieldReference(IFieldReferenceOperation operation, object? argument)
            {
                var value = base.VisitFieldReference(operation, argument);
 
                // "class SomeType { static readonly SomeType Empty; }"
                if (IsSpecialEmptyMember(operation.Field) &&
                    value.NullState != NullAbstractValue.Null)
                {
                    return value.MakeNonNull();
                }
 
                return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value);
            }
 
            public override PointsToAbstractValue VisitPropertyReference(IPropertyReferenceOperation operation, object? argument)
            {
                var value = base.VisitPropertyReference(operation, argument);
 
                // "class SomeType { static SomeType Empty { get; } }"
                if (IsSpecialEmptyMember(operation.Property) &&
                    value.NullState != NullAbstractValue.Null)
                {
                    return value.MakeNonNull();
                }
 
                return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value);
            }
 
            public override PointsToAbstractValue VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation, object? argument)
            {
                var value = base.VisitDynamicMemberReference(operation, argument);
                return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value);
            }
 
            public override PointsToAbstractValue VisitMethodReference(IMethodReferenceOperation operation, object? argument)
            {
                var value = base.VisitMethodReference(operation, argument);
                return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value);
            }
 
            public override PointsToAbstractValue VisitEventReference(IEventReferenceOperation operation, object? argument)
            {
                var value = base.VisitEventReference(operation, argument);
                return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value);
            }
 
            public override PointsToAbstractValue VisitArrayElementReference(IArrayElementReferenceOperation operation, object? argument)
            {
                var value = base.VisitArrayElementReference(operation, argument);
                return GetValueBasedOnInstanceOrReferenceValue(operation.ArrayReference, operation, value);
            }
 
            public override PointsToAbstractValue VisitDynamicIndexerAccess(IDynamicIndexerAccessOperation operation, object? argument)
            {
                var value = base.VisitDynamicIndexerAccess(operation, argument)!;
                return GetValueBasedOnInstanceOrReferenceValue(operation.Operation, operation, value);
            }
 
            public override PointsToAbstractValue VisitConversion(IConversionOperation operation, object? argument)
            {
                var value = base.VisitConversion(operation, argument);
 
                if (operation.OperatorMethod != null)
                {
                    // Conservatively handle user defined conversions as escaping operations.
                    HandleEscapingOperation(operation, operation.Operand);
                    return value;
                }
 
                ConversionInference? inference = null;
                if (value.NullState == NullAbstractValue.NotNull)
                {
                    if (TryInferConversion(operation, out var conversionInference))
                    {
                        inference = conversionInference;
                        value = InferConversionCommon(conversionInference, value);
                    }
                    else
                    {
                        value = value.MakeMayBeNull();
                    }
                }
 
                return HandleBoxingUnboxing(value, operation, inference ?? ConversionInference.Create(operation));
            }
 
            public override PointsToAbstractValue GetAssignedValueForPattern(IIsPatternOperation operation, PointsToAbstractValue operandValue)
            {
                var value = base.GetAssignedValueForPattern(operation, operandValue);
 
                ConversionInference? inference = null;
                if (operandValue.NullState == NullAbstractValue.NotNull &&
                    PointsToAnalysis.ShouldBeTracked(operation.Value.Type, IsDisposable))
                {
                    if (TryInferConversion(operation, out var conversionInference))
                    {
                        inference = conversionInference;
                        value = InferConversionCommon(conversionInference, operandValue);
                    }
                    else
                    {
                        value = operandValue.MakeMayBeNull();
                    }
                }
 
                return HandleBoxingUnboxing(value, operation, inference ?? ConversionInference.Create(operation));
            }
 
            private static PointsToAbstractValue InferConversionCommon(ConversionInference inference, PointsToAbstractValue operandValue)
            {
                Debug.Assert(!inference.AlwaysSucceed || !inference.AlwaysFail);
                if (inference.AlwaysFail)
                {
                    return operandValue.MakeNull();
                }
                else if (inference.IsTryCast && !inference.AlwaysSucceed)
                {
                    // TryCast which may or may not succeed.
                    return operandValue.MakeMayBeNull();
                }
 
                return operandValue;
            }
 
            private PointsToAbstractValue HandleBoxingUnboxing(
                PointsToAbstractValue value,
                IOperation operation,
                ConversionInference inference)
            {
                if (inference.IsBoxing && operation.Type != null)
                {
                    Debug.Assert(!inference.IsUnboxing);
                    var location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext);
                    return PointsToAbstractValue.Create(location, mayBeNull: false);
                }
                else if (inference.IsUnboxing && operation.Type.IsNonNullableValueType())
                {
                    return PointsToAbstractValue.NoLocation;
                }
                else
                {
                    return value;
                }
            }
 
            public override PointsToAbstractValue VisitFlowCapture(IFlowCaptureOperation operation, object? argument)
            {
                var value = base.VisitFlowCapture(operation, argument);
                if (IsLValueFlowCapture(operation) &&
                    AnalysisEntityFactory.TryCreate(operation, out var flowCaptureEntity))
                {
                    value = PointsToAbstractValue.Create(operation.Value);
                    SetAbstractValue(flowCaptureEntity, value);
                }
 
                return value;
            }
 
            public override PointsToAbstractValue VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation, object? argument)
            {
                var value = base.VisitFlowCaptureReference(operation, argument);
                if (IsLValueFlowCaptureReference(operation) &&
                    AnalysisEntityFactory.TryCreate(operation, out var flowCaptureEntity))
                {
                    return GetAbstractValue(flowCaptureEntity);
                }
 
                return value;
            }
 
            public override PointsToAbstractValue ComputeValueForCompoundAssignment(ICompoundAssignmentOperation operation, PointsToAbstractValue targetValue, PointsToAbstractValue assignedValue, ITypeSymbol? targetType, ITypeSymbol? assignedValueType)
            {
                if (targetValue.Kind == PointsToAbstractValueKind.KnownLValueCaptures)
                {
                    // Flow captures can never be re-assigned, so reuse the target value.
                    return targetValue;
                }
 
                return base.ComputeValueForCompoundAssignment(operation, targetValue, assignedValue, targetType, assignedValueType);
            }
 
            protected override PointsToAbstractValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument)
            {
                var value = base.VisitAssignmentOperation(operation, argument);
                HandlePossibleEscapingForAssignment(operation.Target, operation.Value, operation);
                return value;
            }
 
            public override PointsToAbstractValue VisitDeclarationExpression(IDeclarationExpressionOperation operation, object? argument)
            {
                return Visit(operation.Expression, argument);
            }
 
            public override PointsToAbstractValue VisitCaughtException(ICaughtExceptionOperation operation, object? argument)
            {
                _ = base.VisitCaughtException(operation, argument);
                return PointsToAbstractValue.UnknownNotNull;
            }
 
            #endregion
        }
    }
}