File: Analysis\FlowAnalysis\DataFlowsOutWalker.vb
Web Access
Project: src\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 System.Collections.Immutable
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 for
    ''' which their assigned values flow out of the region.
    ''' A variable assigned inside is used outside if an analysis that
    ''' treats assignments in the region as un-assigning the variable would
    ''' cause "unassigned" errors outside the region.
    ''' </summary>
    Friend Class DataFlowsOutWalker
        Inherits AbstractRegionDataFlowPass
 
        Private ReadOnly _dataFlowsIn As ImmutableArray(Of ISymbol)
        Private ReadOnly _originalUnassigned As HashSet(Of Symbol)
        Private ReadOnly _dataFlowsOut As New HashSet(Of Symbol)()
#If DEBUG Then
        ' we'd like to ensure that only variables get returned in DataFlowsOut that were assigned to inside the region.
        Private ReadOnly _assignedInside As HashSet(Of Symbol) = New HashSet(Of Symbol)()
#End If
 
        Private Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo,
                unassignedVariables As HashSet(Of Symbol), originalUnassigned As HashSet(Of Symbol), dataFlowsIn As ImmutableArray(Of ISymbol))
 
            MyBase.New(info, region, unassignedVariables, trackUnassignments:=True, trackStructsWithIntrinsicTypedFields:=True)
 
            Me._dataFlowsIn = dataFlowsIn
            Me._originalUnassigned = originalUnassigned
        End Sub
 
        Friend Overloads Shared Function Analyze(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo,
                                                 unassignedVariables As HashSet(Of Symbol), dataFlowsIn As ImmutableArray(Of ISymbol)) As HashSet(Of Symbol)
 
            ' remove static locals from unassigned, otherwise they will never reach ReportUnassigned(...)
            Dim unassignedWithoutStatic As New HashSet(Of Symbol)
            For Each var In unassignedVariables
                If var.Kind <> SymbolKind.Local OrElse Not DirectCast(var, LocalSymbol).IsStatic Then
                    unassignedWithoutStatic.Add(var)
                End If
            Next
 
            Dim walker = New DataFlowsOutWalker(info, region, unassignedWithoutStatic, unassignedVariables, dataFlowsIn)
            Try
                Dim success = walker.Analyze
                Dim result = walker._dataFlowsOut
#If DEBUG Then
                ' Assert that DataFlowsOut only contains variables that were assigned to inside the region
                Debug.Assert(Not success OrElse Not result.Any(Function(variable) Not walker._assignedInside.Contains(variable)))
#End If
 
                Return If(success, result, New HashSet(Of Symbol)())
            Finally
                walker.Free()
            End Try
        End Function
 
        Protected Overrides Sub EnterRegion()
            ' to handle loops properly, we must assume that every variable (except static locals) 
            ' that flows in is assigned at the beginning of the loop.  If it isn't, then it must 
            ' be in a loop and flow out of the region in that loop (and into the region inside the loop).
            For Each variable As Symbol In _dataFlowsIn
                Dim slot As Integer = Me.GetOrCreateSlot(variable)
                If Not Me.State.IsAssigned(slot) AndAlso variable.Kind <> SymbolKind.RangeVariable AndAlso
                        (variable.Kind <> SymbolKind.Local OrElse Not DirectCast(variable, LocalSymbol).IsStatic) Then
                    _dataFlowsOut.Add(variable)
                End If
            Next
 
            MyBase.EnterRegion()
        End Sub
 
        Protected Overrides Sub Assign(node As BoundNode, value As BoundExpression, Optional assigned As Boolean = True)
            If IsInside Then
 
                If assigned Then
                    Dim symbol = GetNodeSymbol(node)
                    If symbol IsNot Nothing Then
#If DEBUG Then
                        _assignedInside.Add(symbol)
#End If
                    End If
                End If
 
                ' assignments inside the region are recorded as un-assignments
                assigned = False
 
                ' any reachable assignment to a ref or out parameter can 
                ' be visible to the caller in the face of exceptions.
                If Me.State.Reachable Then
                    Select Case node.Kind
 
                        Case BoundKind.Parameter, BoundKind.MeReference
                            Dim expression = DirectCast(node, BoundExpression)
                            Dim slots As SlotCollection = MakeSlotsForExpression(expression)
                            Debug.Assert(slots.Count <= 1)
 
                            If slots.Count > 0 Then
                                Dim slot As Integer = slots(0)
                                If slot >= SlotKind.FirstAvailable Then
 
                                    Dim exprIdentifier = DirectCast(variableBySlot(slot).Symbol, ParameterSymbol)
                                    If exprIdentifier IsNot Nothing AndAlso exprIdentifier.IsByRef Then
                                        _dataFlowsOut.Add(exprIdentifier)
                                    End If
                                End If
                            End If
 
                        Case BoundKind.Local
                            Dim local = DirectCast(node, BoundLocal)
                            Dim locSymbol As LocalSymbol = local.LocalSymbol
                            If locSymbol.IsStatic AndAlso WasUsedBeforeAssignment(locSymbol) Then
                                Dim slots As SlotCollection = MakeSlotsForExpression(local)
                                Debug.Assert(slots.Count <= 1)
 
                                If slots.Count > 0 Then
                                    Dim slot As Integer = slots(0)
                                    If slot >= SlotKind.FirstAvailable Then
                                        Debug.Assert(locSymbol Is variableBySlot(slot).Symbol)
                                        _dataFlowsOut.Add(variableBySlot(slot).Symbol)
                                    End If
                                End If
 
                            End If
 
                    End Select
                End If
            End If
 
            MyBase.Assign(node, value, assigned)
        End Sub
 
        Protected Overrides Sub ReportUnassigned(local As Symbol,
                                                 node As SyntaxNode,
                                                 rwContext As ReadWriteContext,
                                                 Optional slot As Integer = SlotKind.NotTracked,
                                                 Optional boundFieldAccess As BoundFieldAccess = Nothing)
 
            Debug.Assert(local.Kind <> SymbolKind.Field OrElse boundFieldAccess IsNot Nothing)
 
            If Not _dataFlowsOut.Contains(local) AndAlso local.Kind <> SymbolKind.RangeVariable AndAlso Not IsInside Then
                If local.Kind = SymbolKind.Field Then
                    Dim sym As Symbol = GetNodeSymbol(boundFieldAccess)
 
                    ' Unreachable for AmbiguousLocalsPseudoSymbol: ambiguous implicit 
                    ' receiver should not ever be considered unassigned
                    Debug.Assert(Not TypeOf sym Is AmbiguousLocalsPseudoSymbol)
 
                    If sym IsNot Nothing Then
                        _dataFlowsOut.Add(sym)
                    End If
 
                Else
                    _dataFlowsOut.Add(local)
                End If
            End If
 
            MyBase.ReportUnassigned(local, node, rwContext, slot, boundFieldAccess)
        End Sub
 
        Protected Overrides Sub ReportUnassignedByRefParameter(parameter As ParameterSymbol)
            _dataFlowsOut.Add(parameter)
 
            MyBase.ReportUnassignedByRefParameter(parameter)
        End Sub
 
        Protected Overrides Sub NoteWrite(variable As Symbol, value As BoundExpression)
            If Me.State.Reachable Then
                Dim isByRefParameter As Boolean = variable.Kind = SymbolKind.Parameter AndAlso DirectCast(variable, ParameterSymbol).IsByRef
                Dim isStaticLocal As Boolean = variable.Kind = SymbolKind.Local AndAlso DirectCast(variable, LocalSymbol).IsStatic
 
                If IsInside AndAlso (isByRefParameter OrElse isStaticLocal AndAlso WasUsedBeforeAssignment(variable)) Then
                    _dataFlowsOut.Add(variable)
#If DEBUG Then
                    _assignedInside.Add(variable)
#End If
                End If
            End If
 
            MyBase.NoteWrite(variable, value)
        End Sub
 
        Private Function WasUsedBeforeAssignment(sym As Symbol) As Boolean
            Return Me._originalUnassigned.Contains(sym)
        End Function
 
        Friend Overrides Sub AssignLocalOnDeclaration(local As LocalSymbol, node As BoundLocalDeclaration)
            If local.IsStatic Then
                ' We should pretend that all the static locals are 
                ' initialized by previous method invocations
                Assign(node, node.InitializerOpt)
            Else
                MyBase.AssignLocalOnDeclaration(local, node)
            End If
        End Sub
 
    End Class
 
End Namespace