File: FlowAnalysis\AlwaysAssignedWalker.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.
 
#nullable disable
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// A region analysis walker that computes the set of variables that are always assigned a value
    /// in the region. A variable is "always assigned" in a region if an analysis of the region that
    /// starts with the variable unassigned ends with the variable assigned.
    /// </summary>
    internal class AlwaysAssignedWalker : AbstractRegionDataFlowPass
    {
        private LocalState _endOfRegionState;
        private readonly HashSet<LabelSymbol> _labelsInside = new HashSet<LabelSymbol>();
 
        private AlwaysAssignedWalker(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
            : base(compilation, member, node, firstInRegion, lastInRegion)
        {
        }
 
        internal static IEnumerable<Symbol> Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
        {
            var walker = new AlwaysAssignedWalker(compilation, member, node, firstInRegion, lastInRegion);
            bool badRegion = false;
            try
            {
                var result = walker.Analyze(ref badRegion);
                return badRegion ? SpecializedCollections.EmptyEnumerable<Symbol>() : result;
            }
            finally
            {
                walker.Free();
            }
        }
 
        private List<Symbol> Analyze(ref bool badRegion)
        {
            base.Analyze(ref badRegion, null);
            List<Symbol> result = new List<Symbol>();
            Debug.Assert(!IsInside);
            if (_endOfRegionState.Reachable)
            {
                foreach (var i in _endOfRegionState.Assigned.TrueBits())
                {
                    if (i >= variableBySlot.Count)
                    {
                        continue;
                    }
 
                    var v = base.variableBySlot[i];
                    if (v.Exists && !(v.Symbol is FieldSymbol))
                    {
                        result.Add(v.Symbol);
                    }
                }
            }
 
            return result;
        }
 
        protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method)
        {
            // ref parameter does not "always" assign.
            if (refKind == RefKind.Out)
            {
                Assign(arg, value: null);
            }
        }
 
        protected override void ResolveBranch(PendingBranch pending, LabelSymbol label, BoundStatement target, ref bool labelStateChanged)
        {
            // branches into a region are considered entry points
            if (IsInside && pending.Branch != null && !RegionContains(pending.Branch.Syntax.Span))
            {
                pending.State = pending.State.Reachable ? TopState() : UnreachableState();
            }
 
            base.ResolveBranch(pending, label, target, ref labelStateChanged);
        }
 
        public override BoundNode VisitLabel(BoundLabel node)
        {
            ResolveLabel(node, node.Label);
            return base.VisitLabel(node);
        }
 
        public override BoundNode VisitLabeledStatement(BoundLabeledStatement node)
        {
            ResolveLabel(node, node.Label);
            return base.VisitLabeledStatement(node);
        }
 
        private void ResolveLabel(BoundNode node, LabelSymbol label)
        {
            if (node.Syntax != null && RegionContains(node.Syntax.Span)) _labelsInside.Add(label);
        }
 
        protected override LocalState TopState()
        {
            return new LocalState(BitVector.Empty);
        }
 
        protected override void EnterRegion()
        {
            this.State = TopState();
            base.EnterRegion();
        }
 
        protected override void LeaveRegion()
        {
            if (this.IsConditionalState)
            {
                // If the region is in a condition, then the state will be split and state.Assigned will
                // be null.  Merge to get sensible results.
                _endOfRegionState = StateWhenTrue.Clone();
                Join(ref _endOfRegionState, ref StateWhenFalse);
            }
            else
            {
                _endOfRegionState = this.State.Clone();
            }
 
            foreach (var branch in PendingBranches.AsEnumerable())
            {
                if (branch.Branch != null && RegionContains(branch.Branch.Syntax.Span) && !_labelsInside.Contains(branch.Label))
                {
                    Join(ref _endOfRegionState, ref branch.State);
                }
            }
 
            base.LeaveRegion();
        }
    }
}