File: Analysis\FlowAnalysis\AlwaysAssignedWalker.vb
Web Access
Project: src\roslyn\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.

Imports System.Collections.Generic
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic
    ''' <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>
    Friend Class AlwaysAssignedWalker
        Inherits AbstractRegionDataFlowPass

        Private Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo)
            MyBase.New(info, region)
        End Sub

        Friend Overloads Shared Function Analyze(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo) As IEnumerable(Of Symbol)
            Dim walker = New AlwaysAssignedWalker(info, region)
            Try
                Dim result As Boolean = walker.Analyze()
                Return If(result, walker.AlwaysAssigned, SpecializedCollections.EmptyEnumerable(Of Symbol)())
            Finally
                walker.Free()
            End Try
        End Function

        Private _endOfRegionState As LocalState
        Private ReadOnly _labelsInside As New HashSet(Of LabelSymbol)()

        Private ReadOnly Property AlwaysAssigned As IEnumerable(Of Symbol)
            Get
                Dim result As New List(Of Symbol)
                If (_endOfRegionState.Reachable) Then
                    For Each i In _endOfRegionState.Assigned.TrueBits
                        If (i >= variableBySlot.Length) Then
                            Continue For
                        End If

                        Dim v = variableBySlot(i)
                        If v.Exists AndAlso v.Symbol.Kind <> SymbolKind.Field Then
                            result.Add(v.Symbol)
                        End If
                    Next
                End If

                Return result
            End Get
        End Property

        Protected Overrides Sub EnterRegion()
            MyBase.SetState(ReachableState())
            MyBase.EnterRegion()
        End Sub

        Protected Overrides Sub LeaveRegion()
            If Me.IsConditionalState Then
                ' 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 = Me.StateWhenTrue.Clone()
                IntersectWith(_endOfRegionState, Me.StateWhenFalse)
            Else
                _endOfRegionState = MyBase.State.Clone()
            End If

            Debug.Assert(Not _endOfRegionState.Assigned.IsNull)

            For Each branch In PendingBranches
                If IsInsideRegion(branch.Branch.Syntax.Span) AndAlso Not _labelsInside.Contains(branch.Label) Then
                    IntersectWith(_endOfRegionState, branch.State)
                End If
            Next

            MyBase.LeaveRegion()
        End Sub

        Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
            If node.Syntax IsNot Nothing AndAlso IsInsideRegion(node.Syntax.Span) Then
                _labelsInside.Add(node.Label)
            End If

            Return MyBase.VisitLabelStatement(node)
        End Function

        Protected Overrides Sub ResolveBranch(pending As AbstractFlowPass(Of DataFlowPass.LocalState).PendingBranch, label As LabelSymbol, target As BoundLabelStatement, ByRef labelStateChanged As Boolean)
            If IsInside AndAlso pending.Branch IsNot Nothing AndAlso Not IsInsideRegion(pending.Branch.Syntax.Span) Then
                pending.State = If(pending.State.Reachable, ReachableState(), UnreachableState())
            End If

            MyBase.ResolveBranch(pending, label, target, labelStateChanged)
        End Sub

        Protected Overrides Sub WriteArgument(arg As BoundExpression, isOut As Boolean)
            ' ref parameter must be '<Out()>' to "always" assign
            If isOut Then
                MyBase.WriteArgument(arg, isOut)
            End If
        End Sub
    End Class

End Namespace