File: FlowAnalysis\ExitPointsWalker.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.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// A region analysis walker that records jumps out of the region.
    /// </summary>
    internal class ExitPointsWalker : AbstractRegionControlFlowPass
    {
        private readonly ArrayBuilder<LabelSymbol> _labelsInside;
        private readonly ArrayBuilder<StatementSyntax> _branchesOutOf;
 
        private ExitPointsWalker(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
            : base(compilation, member, node, firstInRegion, lastInRegion)
        {
            _labelsInside = new ArrayBuilder<LabelSymbol>();
            _branchesOutOf = ArrayBuilder<StatementSyntax>.GetInstance();
        }
 
        protected override void Free()
        {
            if (_branchesOutOf != null)
            {
                _branchesOutOf.Free();
            }
 
            _labelsInside.Free();
            base.Free();
        }
 
        internal static ImmutableArray<StatementSyntax> Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
        {
            var walker = new ExitPointsWalker(compilation, member, node, firstInRegion, lastInRegion);
            try
            {
                return walker.Analyze();
            }
            finally
            {
                walker.Free();
            }
        }
 
        private ImmutableArray<StatementSyntax> Analyze()
        {
            bool badRegion = false;
 
            // only one pass is needed.
            Scan(ref badRegion);
 
            if (badRegion)
            {
                return ImmutableArray<StatementSyntax>.Empty;
            }
 
            _branchesOutOf.Sort((x, y) => x.SpanStart - y.SpanStart);
            return _branchesOutOf.ToImmutable();
        }
 
        public override BoundNode VisitLabelStatement(BoundLabelStatement node)
        {
            if (IsInside) _labelsInside.Add(node.Label);
            return base.VisitLabelStatement(node);
        }
 
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            if (IsInside)
            {
                _labelsInside.Add(node.BreakLabel);
                _labelsInside.Add(node.ContinueLabel);
            }
            return base.VisitDoStatement(node);
        }
 
        public override BoundNode VisitForEachStatement(BoundForEachStatement node)
        {
            if (IsInside)
            {
                _labelsInside.Add(node.BreakLabel);
                _labelsInside.Add(node.ContinueLabel);
            }
            return base.VisitForEachStatement(node);
        }
 
        public override BoundNode VisitForStatement(BoundForStatement node)
        {
            if (IsInside)
            {
                _labelsInside.Add(node.BreakLabel);
                _labelsInside.Add(node.ContinueLabel);
            }
            return base.VisitForStatement(node);
        }
 
        public override BoundNode VisitWhileStatement(BoundWhileStatement node)
        {
            if (IsInside)
            {
                _labelsInside.Add(node.BreakLabel);
            }
            return base.VisitWhileStatement(node);
        }
 
        protected override void EnterRegion()
        {
            base.EnterRegion();
        }
 
        protected override void LeaveRegion()
        {
            foreach (var pending in PendingBranches.AsEnumerable())
            {
                if (pending.Branch == null || !RegionContains(pending.Branch.Syntax.Span)) continue;
                switch (pending.Branch.Kind)
                {
                    case BoundKind.GotoStatement:
                        if (_labelsInside.Contains(((BoundGotoStatement)pending.Branch).Label)) continue;
                        break;
                    case BoundKind.BreakStatement:
                        if (_labelsInside.Contains(((BoundBreakStatement)pending.Branch).Label)) continue;
                        break;
                    case BoundKind.ContinueStatement:
                        if (_labelsInside.Contains(((BoundContinueStatement)pending.Branch).Label)) continue;
                        break;
                    case BoundKind.YieldBreakStatement:
                    case BoundKind.ReturnStatement:
                        // Return statements are included
                        break;
                    case BoundKind.YieldReturnStatement:
                    case BoundKind.AwaitExpression:
                    case BoundKind.UsingStatement:
                    case BoundKind.ForEachStatement when ((BoundForEachStatement)pending.Branch).AwaitOpt != null:
                        // We don't do anything with yield return statements, async using statement, async foreach statement, or await expressions;
                        // they are treated as if they are not jumps.
                        continue;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(pending.Branch.Kind);
                }
                _branchesOutOf.Add((StatementSyntax)pending.Branch.Syntax);
            }
 
            base.LeaveRegion();
        }
    }
}