<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal abstract partial class PreciseAbstractFlowPass<LocalState> : BoundTreeVisitor
        where LocalState : AbstractFlowPass<LocalState>.AbstractLocalState
    {
        protected int _recursionDepth;

        /// <summary>
        /// The compilation in which the analysis is taking place.  This is needed to determine which
        /// conditional methods will be compiled and which will be omitted.
        /// </summary>
        protected readonly CSharpCompilation compilation;

        /// <summary>
        /// The method whose body is being analyzed, or the field whose initializer is being analyzed.
        /// It is used for
        /// references to method parameters. Thus, 'member' should not be used directly, but
        /// 'MethodParameters', 'MethodThisParameter' and 'AnalyzeOutParameters(...)' should be used
        /// instead.
        /// </summary>
        private readonly Symbol _member;

        /// <summary>
        /// The bound node of the method or initializer being analyzed.
        /// </summary>
        protected readonly BoundNode methodMainNode;

        /// <summary>
        /// The flow analysis state at each label, computed by merging the state from branches to
        /// that label with the state when we fall into the label.  Entries are created when the
        /// label is encountered.  One case deserves special attention: when the destination of the
        /// branch is a label earlier in the code, it is possible (though rarely occurs in practice)
        /// that we are changing the state at a label that we've already analyzed. In that case we
        /// run another pass of the analysis to allow those changes to propagate. This repeats until
        /// no further changes to the state of these labels occurs.  This can result in quadratic
        /// performance in unlikely but possible code such as this: "int x; if (cond) goto l1; x =
        /// 3; l5: print x; l4: goto l5; l3: goto l4; l2: goto l3; l1: goto l2;"
        /// </summary>
        private readonly PooledDictionary<LabelSymbol, LocalState> _labels;

        /// <summary>
        /// Set to true after an analysis scan if the analysis was incomplete due to a backward
        /// "goto" branch changing some analysis result.  In this case the caller scans again (until
        /// this is false). Since the analysis proceeds by monotonically changing the state computed
        /// at each label, this must terminate.
        /// </summary>
        internal bool backwardBranchChanged;

        /// <summary>
        /// See property PendingBranches
        /// </summary>
        private ArrayBuilder<PendingBranch> _pendingBranches;

        /// <summary>
        /// All of the labels seen so far in this forward scan of the body
        /// </summary>
        private PooledHashSet<BoundStatement> _labelsSeen;

        /// <summary>
        /// If we are tracking exceptions, then by convention the first entry in the pending branches
        /// buffer contains a summary of the states that can arise from exceptions.
        /// </summary>
        private readonly bool _trackExceptions;

        /// <summary>
        /// Pending escapes generated in the current scope (or more deeply nested scopes). When jump
        /// statements (goto, break, continue, return) are processed, they are placed in the
        /// pendingBranches buffer to be processed later by the code handling the destination
        /// statement. As a special case, the processing of try-finally statements might modify the
        /// contents of the pendingBranches buffer to take into account the behavior of
        /// "intervening" finally clauses.
        /// </summary>
        protected ArrayBuilder<PendingBranch> PendingBranches
        {
            get
            {
                return _pendingBranches;
            }
        }

        /// <summary>
        /// The definite assignment and/or reachability state at the point currently being analyzed.
        /// </summary>
        protected LocalState State;
        protected LocalState StateWhenTrue;
        protected LocalState StateWhenFalse;
        protected bool IsConditionalState;

        protected void SetConditionalState(LocalState whenTrue, LocalState whenFalse)
        {
            IsConditionalState = true;
            State = default(LocalState);
            StateWhenTrue = whenTrue;
            StateWhenFalse = whenFalse;
        }

        protected void SetState(LocalState newState)
        {
            StateWhenTrue = StateWhenFalse = default(LocalState);
            IsConditionalState = false;
            State = newState;
        }

        protected void Split()
        {
            Debug.Assert(!_trackExceptions || _pendingBranches[0].Branch == null);
            if (!IsConditionalState)
            {
                SetConditionalState(State, State.Clone());
            }
        }

        protected void Unsplit()
        {
            Debug.Assert(!_trackExceptions || _pendingBranches[0].Branch == null);
            if (IsConditionalState)
            {
                IntersectWith(ref StateWhenTrue, ref StateWhenFalse);
                SetState(StateWhenTrue);
            }
        }

        /// <summary>
        /// Where all diagnostics are deposited.
        /// </summary>
        protected DiagnosticBag Diagnostics { get; }

        #region Region
        // For region analysis, we maintain some extra data.
        protected enum RegionPlace { Before, Inside, After };
        protected RegionPlace regionPlace; // tells whether we are currently analyzing code before, during, or after the region
        protected readonly BoundNode firstInRegion, lastInRegion;
        private readonly bool _trackRegions;

        /// <summary>
        /// A cache of the state at the backward branch point of each loop.  This is not needed
        /// during normal flow analysis, but is needed for DataFlowsOut region analysis.
        /// </summary>
        private readonly Dictionary<BoundLoopStatement, LocalState> _loopHeadState;
        #endregion Region

        protected PreciseAbstractFlowPass(
            CSharpCompilation compilation,
            Symbol member,
            BoundNode node,
            BoundNode firstInRegion = null,
            BoundNode lastInRegion = null,
            bool trackRegions = false,
            bool trackExceptions = false)
        {
            Debug.Assert(node != null);

            if (firstInRegion != null && lastInRegion != null)
            {
                trackRegions = true;
            }

            if (trackRegions)
            {
                Debug.Assert(firstInRegion != null);
                Debug.Assert(lastInRegion != null);
                int startLocation = firstInRegion.Syntax.SpanStart;
                int endLocation = lastInRegion.Syntax.Span.End;
                int length = endLocation - startLocation;
                Debug.Assert(length > 0, "last comes before first");
                this.RegionSpan = new TextSpan(startLocation, length);
            }

            _pendingBranches = ArrayBuilder<PendingBranch>.GetInstance();
            _labelsSeen = PooledHashSet<BoundStatement>.GetInstance();
            _labels = PooledDictionary<LabelSymbol, LocalState>.GetInstance();
            this.Diagnostics = DiagnosticBag.GetInstance();
            this.compilation = compilation;
            _member = member;
            this.methodMainNode = node;
            this.firstInRegion = firstInRegion;
            this.lastInRegion = lastInRegion;
            _loopHeadState = new Dictionary<BoundLoopStatement, LocalState>(ReferenceEqualityComparer.Instance);
            _trackRegions = trackRegions;
            _trackExceptions = trackExceptions;
        }

        protected abstract string Dump(LocalState state);

        protected string Dump()
        {
            return Dump(this.State);
        }

#if DEBUG
        protected string DumpLabels()
        {
            StringBuilder result = new StringBuilder();
            result.Append("Labels{");
            bool first = true;
            foreach (var key in _labels.Keys)
            {
                if (!first) result.Append(", ");
                string name = key.Name;
                if (string.IsNullOrEmpty(name)) name = "<Label>" + key.GetHashCode();
                result.Append(name).Append(": ").Append(this.Dump(_labels[key]));<commit_msg>Improved Dump() for conditional states in the flow pass.<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal abstract partial class PreciseAbstractFlowPass<LocalState> : BoundTreeVisitor
        where LocalState : AbstractFlowPass<LocalState>.AbstractLocalState
    {
        protected int _recursionDepth;

        /// <summary>
        /// The compilation in which the analysis is taking place.  This is needed to determine which
        /// conditional methods will be compiled and which will be omitted.
        /// </summary>
        protected readonly CSharpCompilation compilation;

        /// <summary>
        /// The method whose body is being analyzed, or the field whose initializer is being analyzed.
        /// It is used for
        /// references to method parameters. Thus, 'member' should not be used directly, but
        /// 'MethodParameters', 'MethodThisParameter' and 'AnalyzeOutParameters(...)' should be used
        /// instead.
        /// </summary>
        private readonly Symbol _member;

        /// <summary>
        /// The bound node of the method or initializer being analyzed.
        /// </summary>
        protected readonly BoundNode methodMainNode;

        /// <summary>
        /// The flow analysis state at each label, computed by merging the state from branches to
        /// that label with the state when we fall into the label.  Entries are created when the
        /// label is encountered.  One case deserves special attention: when the destination of the
        /// branch is a label earlier in the code, it is possible (though rarely occurs in practice)
        /// that we are changing the state at a label that we've already analyzed. In that case we
        /// run another pass of the analysis to allow those changes to propagate. This repeats until
        /// no further changes to the state of these labels occurs.  This can result in quadratic
        /// performance in unlikely but possible code such as this: "int x; if (cond) goto l1; x =
        /// 3; l5: print x; l4: goto l5; l3: goto l4; l2: goto l3; l1: goto l2;"
        /// </summary>
        private readonly PooledDictionary<LabelSymbol, LocalState> _labels;

        /// <summary>
        /// Set to true after an analysis scan if the analysis was incomplete due to a backward
        /// "goto" branch changing some analysis result.  In this case the caller scans again (until
        /// this is false). Since the analysis proceeds by monotonically changing the state computed
        /// at each label, this must terminate.
        /// </summary>
        internal bool backwardBranchChanged;

        /// <summary>
        /// See property PendingBranches
        /// </summary>
        private ArrayBuilder<PendingBranch> _pendingBranches;

        /// <summary>
        /// All of the labels seen so far in this forward scan of the body
        /// </summary>
        private PooledHashSet<BoundStatement> _labelsSeen;

        /// <summary>
        /// If we are tracking exceptions, then by convention the first entry in the pending branches
        /// buffer contains a summary of the states that can arise from exceptions.
        /// </summary>
        private readonly bool _trackExceptions;

        /// <summary>
        /// Pending escapes generated in the current scope (or more deeply nested scopes). When jump
        /// statements (goto, break, continue, return) are processed, they are placed in the
        /// pendingBranches buffer to be processed later by the code handling the destination
        /// statement. As a special case, the processing of try-finally statements might modify the
        /// contents of the pendingBranches buffer to take into account the behavior of
        /// "intervening" finally clauses.
        /// </summary>
        protected ArrayBuilder<PendingBranch> PendingBranches
        {
            get
            {
                return _pendingBranches;
            }
        }

        /// <summary>
        /// The definite assignment and/or reachability state at the point currently being analyzed.
        /// </summary>
        protected LocalState State;
        protected LocalState StateWhenTrue;
        protected LocalState StateWhenFalse;
        protected bool IsConditionalState;

        protected void SetConditionalState(LocalState whenTrue, LocalState whenFalse)
        {
            IsConditionalState = true;
            State = default(LocalState);
            StateWhenTrue = whenTrue;
            StateWhenFalse = whenFalse;
        }

        protected void SetState(LocalState newState)
        {
            StateWhenTrue = StateWhenFalse = default(LocalState);
            IsConditionalState = false;
            State = newState;
        }

        protected void Split()
        {
            Debug.Assert(!_trackExceptions || _pendingBranches[0].Branch == null);
            if (!IsConditionalState)
            {
                SetConditionalState(State, State.Clone());
            }
        }

        protected void Unsplit()
        {
            Debug.Assert(!_trackExceptions || _pendingBranches[0].Branch == null);
            if (IsConditionalState)
            {
                IntersectWith(ref StateWhenTrue, ref StateWhenFalse);
                SetState(StateWhenTrue);
            }
        }

        /// <summary>
        /// Where all diagnostics are deposited.
        /// </summary>
        protected DiagnosticBag Diagnostics { get; }

        #region Region
        // For region analysis, we maintain some extra data.
        protected enum RegionPlace { Before, Inside, After };
        protected RegionPlace regionPlace; // tells whether we are currently analyzing code before, during, or after the region
        protected readonly BoundNode firstInRegion, lastInRegion;
        private readonly bool _trackRegions;

        /// <summary>
        /// A cache of the state at the backward branch point of each loop.  This is not needed
        /// during normal flow analysis, but is needed for DataFlowsOut region analysis.
        /// </summary>
        private readonly Dictionary<BoundLoopStatement, LocalState> _loopHeadState;
        #endregion Region

        protected PreciseAbstractFlowPass(
            CSharpCompilation compilation,
            Symbol member,
            BoundNode node,
            BoundNode firstInRegion = null,
            BoundNode lastInRegion = null,
            bool trackRegions = false,
            bool trackExceptions = false)
        {
            Debug.Assert(node != null);

            if (firstInRegion != null && lastInRegion != null)
            {
                trackRegions = true;
            }

            if (trackRegions)
            {
                Debug.Assert(firstInRegion != null);
                Debug.Assert(lastInRegion != null);
                int startLocation = firstInRegion.Syntax.SpanStart;
                int endLocation = lastInRegion.Syntax.Span.End;
                int length = endLocation - startLocation;
                Debug.Assert(length > 0, "last comes before first");
                this.RegionSpan = new TextSpan(startLocation, length);
            }

            _pendingBranches = ArrayBuilder<PendingBranch>.GetInstance();
            _labelsSeen = PooledHashSet<BoundStatement>.GetInstance();
            _labels = PooledDictionary<LabelSymbol, LocalState>.GetInstance();
            this.Diagnostics = DiagnosticBag.GetInstance();
            this.compilation = compilation;
            _member = member;
            this.methodMainNode = node;
            this.firstInRegion = firstInRegion;
            this.lastInRegion = lastInRegion;
            _loopHeadState = new Dictionary<BoundLoopStatement, LocalState>(ReferenceEqualityComparer.Instance);
            _trackRegions = trackRegions;
            _trackExceptions = trackExceptions;
        }

        protected abstract string Dump(LocalState state);

        protected string Dump()
        {
            return IsConditionalState
                ? $"true: {Dump(this.StateWhenTrue)} false: {Dump(this.StateWhenFalse)}"
                : Dump(this.State);
        }

#if DEBUG
        protected string DumpLabels()
        {
            StringBuilder result = new StringBuilder();
            result.Append("Labels{");
            bool first = true;
            foreach (var key in _labels.Keys)
            {
                if (!first) result.Append(", ");
                string name = key.Name;
                if (string.IsNullOrEmpty(name)) name = "<Label>" + key.GetHashCode();
                result.Append(name).Append(": ").Append(this.Dump(_labels[key]));
{'fraction_non_alphanumeric': 0.07996460764253718, 'fraction_numerical': 0.0017696178731405187, 'mean_word_length': 2.049064238745574, 'pattern_counts': {'":': 2, '<': 75, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2144425', 'n_tokens_mistral': 4265, 'n_tokens_neox': 4148, 'n_words': 1546}